summaryrefslogtreecommitdiff
path: root/macros
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2021-06-03 03:02:31 +0000
committerNorbert Preining <norbert@preining.info>2021-06-03 03:02:31 +0000
commitd231c6331f53a048d29c2f847254a9f56d5ee2dd (patch)
treec8b2637a679254e40e8d4da5c03fda92cfaa7974 /macros
parent138d0a6cfd5f2187973fb6b83ad2fb0dcf5a8348 (diff)
CTAN sync 202106030302
Diffstat (limited to 'macros')
-rw-r--r--macros/latex/base/alltt.pdfbin214710 -> 214710 bytes
-rw-r--r--macros/latex/base/cfgguide.pdfbin242886 -> 242886 bytes
-rw-r--r--macros/latex/base/classes.pdfbin550002 -> 550002 bytes
-rw-r--r--macros/latex/base/clsguide.pdfbin402779 -> 402779 bytes
-rw-r--r--macros/latex/base/cmfonts.pdfbin292317 -> 292317 bytes
-rw-r--r--macros/latex/base/cyrguide.pdfbin239681 -> 239681 bytes
-rw-r--r--macros/latex/base/doc.pdfbin676501 -> 676501 bytes
-rw-r--r--macros/latex/base/docstrip.pdfbin503186 -> 503186 bytes
-rw-r--r--macros/latex/base/encguide.pdfbin1316472 -> 1316472 bytes
-rw-r--r--macros/latex/base/exscale.pdfbin212307 -> 212307 bytes
-rw-r--r--macros/latex/base/fix-cm.pdfbin287672 -> 287672 bytes
-rw-r--r--macros/latex/base/fntguide.pdfbin493957 -> 493957 bytes
-rw-r--r--macros/latex/base/graphpap.pdfbin169323 -> 169323 bytes
-rw-r--r--macros/latex/base/ifthen.pdfbin220417 -> 220417 bytes
-rw-r--r--macros/latex/base/inputenc.pdfbin318168 -> 318168 bytes
-rw-r--r--macros/latex/base/latexchanges.pdfbin468043 -> 468043 bytes
-rw-r--r--macros/latex/base/latexrelease.pdfbin290495 -> 290495 bytes
-rw-r--r--macros/latex/base/latexsym.pdfbin208359 -> 208359 bytes
-rw-r--r--macros/latex/base/lb2.pdfbin252154 -> 252154 bytes
-rw-r--r--macros/latex/base/letter.pdfbin328728 -> 328728 bytes
-rw-r--r--macros/latex/base/lgc2.pdfbin215091 -> 215091 bytes
-rw-r--r--macros/latex/base/lppl.pdfbin132373 -> 132373 bytes
-rw-r--r--macros/latex/base/ltcmdhooks-code.pdfbin693019 -> 693019 bytes
-rw-r--r--macros/latex/base/ltcmdhooks-doc.pdfbin479331 -> 479331 bytes
-rw-r--r--macros/latex/base/ltfilehook-code.pdfbin795324 -> 795324 bytes
-rw-r--r--macros/latex/base/ltfilehook-doc.pdfbin545883 -> 545883 bytes
-rw-r--r--macros/latex/base/lthooks-code.pdfbin1029072 -> 1029068 bytes
-rw-r--r--macros/latex/base/lthooks-doc.pdfbin683789 -> 683789 bytes
-rw-r--r--macros/latex/base/ltluatex.pdfbin325289 -> 325289 bytes
-rw-r--r--macros/latex/base/ltnews.pdfbin1074364 -> 1074364 bytes
-rw-r--r--macros/latex/base/ltnews01.pdfbin161518 -> 161518 bytes
-rw-r--r--macros/latex/base/ltnews02.pdfbin150466 -> 150466 bytes
-rw-r--r--macros/latex/base/ltnews03.pdfbin150493 -> 150493 bytes
-rw-r--r--macros/latex/base/ltnews04.pdfbin143686 -> 143686 bytes
-rw-r--r--macros/latex/base/ltnews05.pdfbin174488 -> 174488 bytes
-rw-r--r--macros/latex/base/ltnews06.pdfbin142909 -> 142909 bytes
-rw-r--r--macros/latex/base/ltnews07.pdfbin138044 -> 138044 bytes
-rw-r--r--macros/latex/base/ltnews08.pdfbin155055 -> 155055 bytes
-rw-r--r--macros/latex/base/ltnews09.pdfbin129032 -> 129032 bytes
-rw-r--r--macros/latex/base/ltnews10.pdfbin159313 -> 159313 bytes
-rw-r--r--macros/latex/base/ltnews11.pdfbin111955 -> 111955 bytes
-rw-r--r--macros/latex/base/ltnews12.pdfbin147587 -> 147587 bytes
-rw-r--r--macros/latex/base/ltnews13.pdfbin173323 -> 173323 bytes
-rw-r--r--macros/latex/base/ltnews14.pdfbin161405 -> 161405 bytes
-rw-r--r--macros/latex/base/ltnews15.pdfbin130447 -> 130447 bytes
-rw-r--r--macros/latex/base/ltnews16.pdfbin146533 -> 146533 bytes
-rw-r--r--macros/latex/base/ltnews17.pdfbin166368 -> 166368 bytes
-rw-r--r--macros/latex/base/ltnews18.pdfbin38031 -> 38031 bytes
-rw-r--r--macros/latex/base/ltnews19.pdfbin245818 -> 245818 bytes
-rw-r--r--macros/latex/base/ltnews20.pdfbin297070 -> 297070 bytes
-rw-r--r--macros/latex/base/ltnews21.pdfbin244869 -> 244869 bytes
-rw-r--r--macros/latex/base/ltnews22.pdfbin318399 -> 318399 bytes
-rw-r--r--macros/latex/base/ltnews23.pdfbin287125 -> 287125 bytes
-rw-r--r--macros/latex/base/ltnews24.pdfbin265211 -> 265211 bytes
-rw-r--r--macros/latex/base/ltnews25.pdfbin205162 -> 205162 bytes
-rw-r--r--macros/latex/base/ltnews26.pdfbin379735 -> 379735 bytes
-rw-r--r--macros/latex/base/ltnews27.pdfbin252432 -> 252432 bytes
-rw-r--r--macros/latex/base/ltnews28.pdfbin387987 -> 387987 bytes
-rw-r--r--macros/latex/base/ltnews29.pdfbin454808 -> 454808 bytes
-rw-r--r--macros/latex/base/ltnews30.pdfbin511855 -> 511855 bytes
-rw-r--r--macros/latex/base/ltnews31.pdfbin526312 -> 526312 bytes
-rw-r--r--macros/latex/base/ltnews32.pdfbin546162 -> 546162 bytes
-rw-r--r--macros/latex/base/ltnews33.pdfbin522411 -> 522411 bytes
-rw-r--r--macros/latex/base/ltpara-code.pdfbin664444 -> 664444 bytes
-rw-r--r--macros/latex/base/ltpara-doc.pdfbin560166 -> 560166 bytes
-rw-r--r--macros/latex/base/ltshipout-code.pdfbin818625 -> 818625 bytes
-rw-r--r--macros/latex/base/ltshipout-doc.pdfbin564572 -> 564572 bytes
-rw-r--r--macros/latex/base/ltvers.dtx2
-rw-r--r--macros/latex/base/ltx3info.pdfbin219354 -> 219354 bytes
-rw-r--r--macros/latex/base/ltxdoc.pdfbin238577 -> 238577 bytes
-rw-r--r--macros/latex/base/makeindx.pdfbin173682 -> 173682 bytes
-rw-r--r--macros/latex/base/manual.pdfbin160521 -> 160521 bytes
-rw-r--r--macros/latex/base/modguide.pdfbin243601 -> 243601 bytes
-rw-r--r--macros/latex/base/nfssfont.pdfbin218782 -> 218782 bytes
-rw-r--r--macros/latex/base/proc.pdfbin233995 -> 233995 bytes
-rw-r--r--macros/latex/base/slides.pdfbin374051 -> 374051 bytes
-rw-r--r--macros/latex/base/slifonts.pdfbin234008 -> 234008 bytes
-rw-r--r--macros/latex/base/source2e.pdfbin5275730 -> 5276053 bytes
-rw-r--r--macros/latex/base/syntonly.pdfbin197187 -> 197187 bytes
-rw-r--r--macros/latex/base/tlc2.pdfbin289245 -> 289245 bytes
-rw-r--r--macros/latex/base/tulm.pdfbin170529 -> 170529 bytes
-rw-r--r--macros/latex/base/usrguide.pdfbin430996 -> 430996 bytes
-rw-r--r--macros/latex/base/usrguide3.pdfbin325299 -> 325299 bytes
-rw-r--r--macros/latex/base/utf8ienc.pdfbin361477 -> 361477 bytes
-rw-r--r--macros/latex/base/webcomp.pdfbin146200 -> 146200 bytes
-rw-r--r--macros/latex/contrib/biblatex-contrib/biblatex-cv/README.md20
-rw-r--r--macros/latex/contrib/biblatex-contrib/biblatex-cv/american-cv.lbx53
-rw-r--r--macros/latex/contrib/biblatex-contrib/biblatex-cv/biblatex-cv.bbx736
-rw-r--r--macros/latex/contrib/biblatex-contrib/biblatex-cv/biblatex-cv.cbx3
-rw-r--r--macros/latex/contrib/biblatex-contrib/biblatex-cv/biblatex-cv.dbx386
-rw-r--r--macros/latex/contrib/biblatex-contrib/biblatex-cv/biblatex-cv.sty776
-rw-r--r--macros/latex/contrib/biblatex-contrib/biblatex-cv/doc/biblatex-cv.bib347
-rw-r--r--macros/latex/contrib/biblatex-contrib/biblatex-cv/doc/biblatex-cv.pdfbin0 -> 99344 bytes
-rw-r--r--macros/latex/contrib/biblatex-contrib/biblatex-cv/doc/biblatex-cv.tex416
-rw-r--r--macros/latex/contrib/biblatex-contrib/biblatex-cv/doc/cv.pdfbin0 -> 59226 bytes
-rw-r--r--macros/latex/contrib/biblatex-contrib/biblatex-cv/doc/cv.tex40
-rw-r--r--macros/latex/contrib/easybook/easybook.dtx647
-rw-r--r--macros/latex/contrib/easybook/easybook.ins6
-rw-r--r--macros/latex/contrib/easybook/easybook.pdfbin313301 -> 315453 bytes
-rw-r--r--macros/latex/required/babel/base/README.md18
-rw-r--r--macros/latex/required/babel/base/babel.dtx309
-rw-r--r--macros/latex/required/babel/base/babel.ins2
-rw-r--r--macros/latex/required/babel/base/babel.pdfbin844087 -> 843759 bytes
-rw-r--r--macros/latex/required/babel/base/bbcompat.dtx2
-rw-r--r--macros/luatex/latex/lua-typo/README.md24
-rw-r--r--macros/luatex/latex/lua-typo/doc/lua-typo-demo.pdfbin16794 -> 16794 bytes
-rw-r--r--macros/luatex/latex/lua-typo/doc/lua-typo-fr.pdfbin59619 -> 64171 bytes
-rw-r--r--macros/luatex/latex/lua-typo/doc/lua-typo.pdfbin115609 -> 138442 bytes
-rw-r--r--macros/luatex/latex/lua-typo/lua-typo-2021-04-18.sty843
-rw-r--r--macros/luatex/latex/lua-typo/source/lua-typo.dtx1177
110 files changed, 5020 insertions, 787 deletions
diff --git a/macros/latex/base/alltt.pdf b/macros/latex/base/alltt.pdf
index 1191cad564..7828dd233f 100644
--- a/macros/latex/base/alltt.pdf
+++ b/macros/latex/base/alltt.pdf
Binary files differ
diff --git a/macros/latex/base/cfgguide.pdf b/macros/latex/base/cfgguide.pdf
index 5725de20a5..589df6f24c 100644
--- a/macros/latex/base/cfgguide.pdf
+++ b/macros/latex/base/cfgguide.pdf
Binary files differ
diff --git a/macros/latex/base/classes.pdf b/macros/latex/base/classes.pdf
index 14c42c316e..50dd166f6c 100644
--- a/macros/latex/base/classes.pdf
+++ b/macros/latex/base/classes.pdf
Binary files differ
diff --git a/macros/latex/base/clsguide.pdf b/macros/latex/base/clsguide.pdf
index b9d90891ef..d1cd9bbf46 100644
--- a/macros/latex/base/clsguide.pdf
+++ b/macros/latex/base/clsguide.pdf
Binary files differ
diff --git a/macros/latex/base/cmfonts.pdf b/macros/latex/base/cmfonts.pdf
index 845b56d2ae..26254c3244 100644
--- a/macros/latex/base/cmfonts.pdf
+++ b/macros/latex/base/cmfonts.pdf
Binary files differ
diff --git a/macros/latex/base/cyrguide.pdf b/macros/latex/base/cyrguide.pdf
index bc4155b431..aa8faf6b22 100644
--- a/macros/latex/base/cyrguide.pdf
+++ b/macros/latex/base/cyrguide.pdf
Binary files differ
diff --git a/macros/latex/base/doc.pdf b/macros/latex/base/doc.pdf
index 69994495f4..b257c0f04a 100644
--- a/macros/latex/base/doc.pdf
+++ b/macros/latex/base/doc.pdf
Binary files differ
diff --git a/macros/latex/base/docstrip.pdf b/macros/latex/base/docstrip.pdf
index c212a95d70..f3eceb7c01 100644
--- a/macros/latex/base/docstrip.pdf
+++ b/macros/latex/base/docstrip.pdf
Binary files differ
diff --git a/macros/latex/base/encguide.pdf b/macros/latex/base/encguide.pdf
index 4977c826d2..4b6aea5109 100644
--- a/macros/latex/base/encguide.pdf
+++ b/macros/latex/base/encguide.pdf
Binary files differ
diff --git a/macros/latex/base/exscale.pdf b/macros/latex/base/exscale.pdf
index ef34516e04..f00aff47c4 100644
--- a/macros/latex/base/exscale.pdf
+++ b/macros/latex/base/exscale.pdf
Binary files differ
diff --git a/macros/latex/base/fix-cm.pdf b/macros/latex/base/fix-cm.pdf
index 54def7c13b..f746d23ad6 100644
--- a/macros/latex/base/fix-cm.pdf
+++ b/macros/latex/base/fix-cm.pdf
Binary files differ
diff --git a/macros/latex/base/fntguide.pdf b/macros/latex/base/fntguide.pdf
index bdf6d4a12e..9a661e64e8 100644
--- a/macros/latex/base/fntguide.pdf
+++ b/macros/latex/base/fntguide.pdf
Binary files differ
diff --git a/macros/latex/base/graphpap.pdf b/macros/latex/base/graphpap.pdf
index c1d687ef3c..d71da13a02 100644
--- a/macros/latex/base/graphpap.pdf
+++ b/macros/latex/base/graphpap.pdf
Binary files differ
diff --git a/macros/latex/base/ifthen.pdf b/macros/latex/base/ifthen.pdf
index 478a68c22f..95feb1a73a 100644
--- a/macros/latex/base/ifthen.pdf
+++ b/macros/latex/base/ifthen.pdf
Binary files differ
diff --git a/macros/latex/base/inputenc.pdf b/macros/latex/base/inputenc.pdf
index 76b03a2a67..04b1567faa 100644
--- a/macros/latex/base/inputenc.pdf
+++ b/macros/latex/base/inputenc.pdf
Binary files differ
diff --git a/macros/latex/base/latexchanges.pdf b/macros/latex/base/latexchanges.pdf
index cd61fcfc24..4b73a0285c 100644
--- a/macros/latex/base/latexchanges.pdf
+++ b/macros/latex/base/latexchanges.pdf
Binary files differ
diff --git a/macros/latex/base/latexrelease.pdf b/macros/latex/base/latexrelease.pdf
index e50393e3bd..d80364f316 100644
--- a/macros/latex/base/latexrelease.pdf
+++ b/macros/latex/base/latexrelease.pdf
Binary files differ
diff --git a/macros/latex/base/latexsym.pdf b/macros/latex/base/latexsym.pdf
index eca06019fc..59667fdb70 100644
--- a/macros/latex/base/latexsym.pdf
+++ b/macros/latex/base/latexsym.pdf
Binary files differ
diff --git a/macros/latex/base/lb2.pdf b/macros/latex/base/lb2.pdf
index 2bf5c3afa4..fa48ab7e06 100644
--- a/macros/latex/base/lb2.pdf
+++ b/macros/latex/base/lb2.pdf
Binary files differ
diff --git a/macros/latex/base/letter.pdf b/macros/latex/base/letter.pdf
index 195adf6684..10bac54d14 100644
--- a/macros/latex/base/letter.pdf
+++ b/macros/latex/base/letter.pdf
Binary files differ
diff --git a/macros/latex/base/lgc2.pdf b/macros/latex/base/lgc2.pdf
index a1a3348bca..9327bb182d 100644
--- a/macros/latex/base/lgc2.pdf
+++ b/macros/latex/base/lgc2.pdf
Binary files differ
diff --git a/macros/latex/base/lppl.pdf b/macros/latex/base/lppl.pdf
index 88e9e68c8c..c6fff67f7a 100644
--- a/macros/latex/base/lppl.pdf
+++ b/macros/latex/base/lppl.pdf
Binary files differ
diff --git a/macros/latex/base/ltcmdhooks-code.pdf b/macros/latex/base/ltcmdhooks-code.pdf
index e29001cf29..4a5af8284d 100644
--- a/macros/latex/base/ltcmdhooks-code.pdf
+++ b/macros/latex/base/ltcmdhooks-code.pdf
Binary files differ
diff --git a/macros/latex/base/ltcmdhooks-doc.pdf b/macros/latex/base/ltcmdhooks-doc.pdf
index 1dd0937d57..23f08800cc 100644
--- a/macros/latex/base/ltcmdhooks-doc.pdf
+++ b/macros/latex/base/ltcmdhooks-doc.pdf
Binary files differ
diff --git a/macros/latex/base/ltfilehook-code.pdf b/macros/latex/base/ltfilehook-code.pdf
index 2058ec38af..c221c72425 100644
--- a/macros/latex/base/ltfilehook-code.pdf
+++ b/macros/latex/base/ltfilehook-code.pdf
Binary files differ
diff --git a/macros/latex/base/ltfilehook-doc.pdf b/macros/latex/base/ltfilehook-doc.pdf
index e9611649b2..7aef6cee32 100644
--- a/macros/latex/base/ltfilehook-doc.pdf
+++ b/macros/latex/base/ltfilehook-doc.pdf
Binary files differ
diff --git a/macros/latex/base/lthooks-code.pdf b/macros/latex/base/lthooks-code.pdf
index 27e926f575..4cbacae9ed 100644
--- a/macros/latex/base/lthooks-code.pdf
+++ b/macros/latex/base/lthooks-code.pdf
Binary files differ
diff --git a/macros/latex/base/lthooks-doc.pdf b/macros/latex/base/lthooks-doc.pdf
index 01a03ff1af..03a12565eb 100644
--- a/macros/latex/base/lthooks-doc.pdf
+++ b/macros/latex/base/lthooks-doc.pdf
Binary files differ
diff --git a/macros/latex/base/ltluatex.pdf b/macros/latex/base/ltluatex.pdf
index 93315b577e..e90de35ca7 100644
--- a/macros/latex/base/ltluatex.pdf
+++ b/macros/latex/base/ltluatex.pdf
Binary files differ
diff --git a/macros/latex/base/ltnews.pdf b/macros/latex/base/ltnews.pdf
index c3a22e4f81..3d8d0c8878 100644
--- a/macros/latex/base/ltnews.pdf
+++ b/macros/latex/base/ltnews.pdf
Binary files differ
diff --git a/macros/latex/base/ltnews01.pdf b/macros/latex/base/ltnews01.pdf
index 855b37cb67..cd6b63a090 100644
--- a/macros/latex/base/ltnews01.pdf
+++ b/macros/latex/base/ltnews01.pdf
Binary files differ
diff --git a/macros/latex/base/ltnews02.pdf b/macros/latex/base/ltnews02.pdf
index ceb30b352e..ad0cb50222 100644
--- a/macros/latex/base/ltnews02.pdf
+++ b/macros/latex/base/ltnews02.pdf
Binary files differ
diff --git a/macros/latex/base/ltnews03.pdf b/macros/latex/base/ltnews03.pdf
index cc114b3c2c..40e8f16086 100644
--- a/macros/latex/base/ltnews03.pdf
+++ b/macros/latex/base/ltnews03.pdf
Binary files differ
diff --git a/macros/latex/base/ltnews04.pdf b/macros/latex/base/ltnews04.pdf
index 2fa4552b7f..0fe9f055f5 100644
--- a/macros/latex/base/ltnews04.pdf
+++ b/macros/latex/base/ltnews04.pdf
Binary files differ
diff --git a/macros/latex/base/ltnews05.pdf b/macros/latex/base/ltnews05.pdf
index 2320f76c61..cc4400d9ee 100644
--- a/macros/latex/base/ltnews05.pdf
+++ b/macros/latex/base/ltnews05.pdf
Binary files differ
diff --git a/macros/latex/base/ltnews06.pdf b/macros/latex/base/ltnews06.pdf
index 9dccfbb5dd..1479bf7405 100644
--- a/macros/latex/base/ltnews06.pdf
+++ b/macros/latex/base/ltnews06.pdf
Binary files differ
diff --git a/macros/latex/base/ltnews07.pdf b/macros/latex/base/ltnews07.pdf
index ed48011dc9..08ce725099 100644
--- a/macros/latex/base/ltnews07.pdf
+++ b/macros/latex/base/ltnews07.pdf
Binary files differ
diff --git a/macros/latex/base/ltnews08.pdf b/macros/latex/base/ltnews08.pdf
index 7f06cefc47..e143aa918c 100644
--- a/macros/latex/base/ltnews08.pdf
+++ b/macros/latex/base/ltnews08.pdf
Binary files differ
diff --git a/macros/latex/base/ltnews09.pdf b/macros/latex/base/ltnews09.pdf
index e085006df9..20ef418899 100644
--- a/macros/latex/base/ltnews09.pdf
+++ b/macros/latex/base/ltnews09.pdf
Binary files differ
diff --git a/macros/latex/base/ltnews10.pdf b/macros/latex/base/ltnews10.pdf
index e5022eedcc..b976b23d7b 100644
--- a/macros/latex/base/ltnews10.pdf
+++ b/macros/latex/base/ltnews10.pdf
Binary files differ
diff --git a/macros/latex/base/ltnews11.pdf b/macros/latex/base/ltnews11.pdf
index d6d87f102a..4e3abd44cc 100644
--- a/macros/latex/base/ltnews11.pdf
+++ b/macros/latex/base/ltnews11.pdf
Binary files differ
diff --git a/macros/latex/base/ltnews12.pdf b/macros/latex/base/ltnews12.pdf
index c7f1944e4a..ad97fb4799 100644
--- a/macros/latex/base/ltnews12.pdf
+++ b/macros/latex/base/ltnews12.pdf
Binary files differ
diff --git a/macros/latex/base/ltnews13.pdf b/macros/latex/base/ltnews13.pdf
index 2eb3e5252d..639b4d48e1 100644
--- a/macros/latex/base/ltnews13.pdf
+++ b/macros/latex/base/ltnews13.pdf
Binary files differ
diff --git a/macros/latex/base/ltnews14.pdf b/macros/latex/base/ltnews14.pdf
index 901ed0d704..4755a34f51 100644
--- a/macros/latex/base/ltnews14.pdf
+++ b/macros/latex/base/ltnews14.pdf
Binary files differ
diff --git a/macros/latex/base/ltnews15.pdf b/macros/latex/base/ltnews15.pdf
index b2e8b569c5..fcbafc2f67 100644
--- a/macros/latex/base/ltnews15.pdf
+++ b/macros/latex/base/ltnews15.pdf
Binary files differ
diff --git a/macros/latex/base/ltnews16.pdf b/macros/latex/base/ltnews16.pdf
index 893063bfad..e7cdcd494e 100644
--- a/macros/latex/base/ltnews16.pdf
+++ b/macros/latex/base/ltnews16.pdf
Binary files differ
diff --git a/macros/latex/base/ltnews17.pdf b/macros/latex/base/ltnews17.pdf
index cf3983ba37..6c5d24be55 100644
--- a/macros/latex/base/ltnews17.pdf
+++ b/macros/latex/base/ltnews17.pdf
Binary files differ
diff --git a/macros/latex/base/ltnews18.pdf b/macros/latex/base/ltnews18.pdf
index 83e8a09910..5c5961dcfb 100644
--- a/macros/latex/base/ltnews18.pdf
+++ b/macros/latex/base/ltnews18.pdf
Binary files differ
diff --git a/macros/latex/base/ltnews19.pdf b/macros/latex/base/ltnews19.pdf
index 734b324d0e..81e2906455 100644
--- a/macros/latex/base/ltnews19.pdf
+++ b/macros/latex/base/ltnews19.pdf
Binary files differ
diff --git a/macros/latex/base/ltnews20.pdf b/macros/latex/base/ltnews20.pdf
index 6b226bdffb..827aefe107 100644
--- a/macros/latex/base/ltnews20.pdf
+++ b/macros/latex/base/ltnews20.pdf
Binary files differ
diff --git a/macros/latex/base/ltnews21.pdf b/macros/latex/base/ltnews21.pdf
index 0d13bc374b..8160b4e0bd 100644
--- a/macros/latex/base/ltnews21.pdf
+++ b/macros/latex/base/ltnews21.pdf
Binary files differ
diff --git a/macros/latex/base/ltnews22.pdf b/macros/latex/base/ltnews22.pdf
index 64be8e37ce..5127935414 100644
--- a/macros/latex/base/ltnews22.pdf
+++ b/macros/latex/base/ltnews22.pdf
Binary files differ
diff --git a/macros/latex/base/ltnews23.pdf b/macros/latex/base/ltnews23.pdf
index 9b856dfed5..cb2c5a64b7 100644
--- a/macros/latex/base/ltnews23.pdf
+++ b/macros/latex/base/ltnews23.pdf
Binary files differ
diff --git a/macros/latex/base/ltnews24.pdf b/macros/latex/base/ltnews24.pdf
index bb25bb9195..d564700f53 100644
--- a/macros/latex/base/ltnews24.pdf
+++ b/macros/latex/base/ltnews24.pdf
Binary files differ
diff --git a/macros/latex/base/ltnews25.pdf b/macros/latex/base/ltnews25.pdf
index 61064c6c9e..acf7bb7263 100644
--- a/macros/latex/base/ltnews25.pdf
+++ b/macros/latex/base/ltnews25.pdf
Binary files differ
diff --git a/macros/latex/base/ltnews26.pdf b/macros/latex/base/ltnews26.pdf
index 26a3d6b5aa..956ee877cf 100644
--- a/macros/latex/base/ltnews26.pdf
+++ b/macros/latex/base/ltnews26.pdf
Binary files differ
diff --git a/macros/latex/base/ltnews27.pdf b/macros/latex/base/ltnews27.pdf
index 906f101f3c..fa84da477a 100644
--- a/macros/latex/base/ltnews27.pdf
+++ b/macros/latex/base/ltnews27.pdf
Binary files differ
diff --git a/macros/latex/base/ltnews28.pdf b/macros/latex/base/ltnews28.pdf
index 758cac6345..33771f6de7 100644
--- a/macros/latex/base/ltnews28.pdf
+++ b/macros/latex/base/ltnews28.pdf
Binary files differ
diff --git a/macros/latex/base/ltnews29.pdf b/macros/latex/base/ltnews29.pdf
index 10f239284e..c857c07ba3 100644
--- a/macros/latex/base/ltnews29.pdf
+++ b/macros/latex/base/ltnews29.pdf
Binary files differ
diff --git a/macros/latex/base/ltnews30.pdf b/macros/latex/base/ltnews30.pdf
index d15360dd4d..8ddfcd97a4 100644
--- a/macros/latex/base/ltnews30.pdf
+++ b/macros/latex/base/ltnews30.pdf
Binary files differ
diff --git a/macros/latex/base/ltnews31.pdf b/macros/latex/base/ltnews31.pdf
index 6378f3e9ff..837148c7ce 100644
--- a/macros/latex/base/ltnews31.pdf
+++ b/macros/latex/base/ltnews31.pdf
Binary files differ
diff --git a/macros/latex/base/ltnews32.pdf b/macros/latex/base/ltnews32.pdf
index 6e7b951c9d..d030ff7ebd 100644
--- a/macros/latex/base/ltnews32.pdf
+++ b/macros/latex/base/ltnews32.pdf
Binary files differ
diff --git a/macros/latex/base/ltnews33.pdf b/macros/latex/base/ltnews33.pdf
index f087a6ffcd..3ad7656a15 100644
--- a/macros/latex/base/ltnews33.pdf
+++ b/macros/latex/base/ltnews33.pdf
Binary files differ
diff --git a/macros/latex/base/ltpara-code.pdf b/macros/latex/base/ltpara-code.pdf
index 5e12893ec3..9c1e994325 100644
--- a/macros/latex/base/ltpara-code.pdf
+++ b/macros/latex/base/ltpara-code.pdf
Binary files differ
diff --git a/macros/latex/base/ltpara-doc.pdf b/macros/latex/base/ltpara-doc.pdf
index 75dae10c7d..cf2d7f7c63 100644
--- a/macros/latex/base/ltpara-doc.pdf
+++ b/macros/latex/base/ltpara-doc.pdf
Binary files differ
diff --git a/macros/latex/base/ltshipout-code.pdf b/macros/latex/base/ltshipout-code.pdf
index c2ee874b52..3f678e3710 100644
--- a/macros/latex/base/ltshipout-code.pdf
+++ b/macros/latex/base/ltshipout-code.pdf
Binary files differ
diff --git a/macros/latex/base/ltshipout-doc.pdf b/macros/latex/base/ltshipout-doc.pdf
index 30bb7daef5..a4516f44fb 100644
--- a/macros/latex/base/ltshipout-doc.pdf
+++ b/macros/latex/base/ltshipout-doc.pdf
Binary files differ
diff --git a/macros/latex/base/ltvers.dtx b/macros/latex/base/ltvers.dtx
index af0688b887..515f5afe81 100644
--- a/macros/latex/base/ltvers.dtx
+++ b/macros/latex/base/ltvers.dtx
@@ -125,7 +125,7 @@
% intended to help us internally when we locally install a
% format out of some development branch.
% \begin{macrocode}
-\edef\development@branch@name{develop \the\year-\the\month-\the\day}
+\edef\development@branch@name{}
% \end{macrocode}
% \end{macro}
% \end{macro}
diff --git a/macros/latex/base/ltx3info.pdf b/macros/latex/base/ltx3info.pdf
index 8a85835a8d..0468f98cf3 100644
--- a/macros/latex/base/ltx3info.pdf
+++ b/macros/latex/base/ltx3info.pdf
Binary files differ
diff --git a/macros/latex/base/ltxdoc.pdf b/macros/latex/base/ltxdoc.pdf
index 9308d90656..6320d8fa6f 100644
--- a/macros/latex/base/ltxdoc.pdf
+++ b/macros/latex/base/ltxdoc.pdf
Binary files differ
diff --git a/macros/latex/base/makeindx.pdf b/macros/latex/base/makeindx.pdf
index 130d740e12..53ae6f067b 100644
--- a/macros/latex/base/makeindx.pdf
+++ b/macros/latex/base/makeindx.pdf
Binary files differ
diff --git a/macros/latex/base/manual.pdf b/macros/latex/base/manual.pdf
index 6450d27796..ad1ce6a3ab 100644
--- a/macros/latex/base/manual.pdf
+++ b/macros/latex/base/manual.pdf
Binary files differ
diff --git a/macros/latex/base/modguide.pdf b/macros/latex/base/modguide.pdf
index 30424c7e7a..fc201136d1 100644
--- a/macros/latex/base/modguide.pdf
+++ b/macros/latex/base/modguide.pdf
Binary files differ
diff --git a/macros/latex/base/nfssfont.pdf b/macros/latex/base/nfssfont.pdf
index 93087ad9d6..3ede40bd4f 100644
--- a/macros/latex/base/nfssfont.pdf
+++ b/macros/latex/base/nfssfont.pdf
Binary files differ
diff --git a/macros/latex/base/proc.pdf b/macros/latex/base/proc.pdf
index 3c363516dd..494cc6ce0c 100644
--- a/macros/latex/base/proc.pdf
+++ b/macros/latex/base/proc.pdf
Binary files differ
diff --git a/macros/latex/base/slides.pdf b/macros/latex/base/slides.pdf
index d26b076adb..69429c8683 100644
--- a/macros/latex/base/slides.pdf
+++ b/macros/latex/base/slides.pdf
Binary files differ
diff --git a/macros/latex/base/slifonts.pdf b/macros/latex/base/slifonts.pdf
index f14e61508f..115dea77cf 100644
--- a/macros/latex/base/slifonts.pdf
+++ b/macros/latex/base/slifonts.pdf
Binary files differ
diff --git a/macros/latex/base/source2e.pdf b/macros/latex/base/source2e.pdf
index 73e7c309e2..604e3ac0f4 100644
--- a/macros/latex/base/source2e.pdf
+++ b/macros/latex/base/source2e.pdf
Binary files differ
diff --git a/macros/latex/base/syntonly.pdf b/macros/latex/base/syntonly.pdf
index 44a42ca95c..0a42b26f84 100644
--- a/macros/latex/base/syntonly.pdf
+++ b/macros/latex/base/syntonly.pdf
Binary files differ
diff --git a/macros/latex/base/tlc2.pdf b/macros/latex/base/tlc2.pdf
index 7f340888f1..9889a20ac0 100644
--- a/macros/latex/base/tlc2.pdf
+++ b/macros/latex/base/tlc2.pdf
Binary files differ
diff --git a/macros/latex/base/tulm.pdf b/macros/latex/base/tulm.pdf
index c91b48d517..860d7485ad 100644
--- a/macros/latex/base/tulm.pdf
+++ b/macros/latex/base/tulm.pdf
Binary files differ
diff --git a/macros/latex/base/usrguide.pdf b/macros/latex/base/usrguide.pdf
index bec8542099..32889af019 100644
--- a/macros/latex/base/usrguide.pdf
+++ b/macros/latex/base/usrguide.pdf
Binary files differ
diff --git a/macros/latex/base/usrguide3.pdf b/macros/latex/base/usrguide3.pdf
index 1dc77ecccc..d39963d111 100644
--- a/macros/latex/base/usrguide3.pdf
+++ b/macros/latex/base/usrguide3.pdf
Binary files differ
diff --git a/macros/latex/base/utf8ienc.pdf b/macros/latex/base/utf8ienc.pdf
index 36ad4770ff..59d8c4759d 100644
--- a/macros/latex/base/utf8ienc.pdf
+++ b/macros/latex/base/utf8ienc.pdf
Binary files differ
diff --git a/macros/latex/base/webcomp.pdf b/macros/latex/base/webcomp.pdf
index 488cfb93ec..a723c944ee 100644
--- a/macros/latex/base/webcomp.pdf
+++ b/macros/latex/base/webcomp.pdf
Binary files differ
diff --git a/macros/latex/contrib/biblatex-contrib/biblatex-cv/README.md b/macros/latex/contrib/biblatex-contrib/biblatex-cv/README.md
new file mode 100644
index 0000000000..4134b879fc
--- /dev/null
+++ b/macros/latex/contrib/biblatex-contrib/biblatex-cv/README.md
@@ -0,0 +1,20 @@
+Version: 0.01
+
+# Overview
+
+This LaTeX package provides an interface for creating an academic curriculum vitae (CV) from a BibTeX .bib file. The package makes use of biblatex/biber to automatically format, group, and sort the entries on a CV. Package documentation and an example CV are provided in the doc folder.
+
+# Copyright and Licence
+
+## Authors
+- Daniel E. Shub
+
+## Copyright
+- Copyright 2019 --- ... Daniel E. Shub
+
+## Licence
+This work may be distributed and/or modified under the conditions of the LaTeX Project Public License, either version 1.3 of this license or (at your option) any later version.
+
+The latest version of the license is in [https://www.latex-project.org/lppl.txt](https://www.latex-project.org/lppl.txt) and version 1.3 or later is part of all distributions of LaTeX version 2003/06/01 or later.
+
+This work has the LPPL maintenance status "maintained".
diff --git a/macros/latex/contrib/biblatex-contrib/biblatex-cv/american-cv.lbx b/macros/latex/contrib/biblatex-contrib/biblatex-cv/american-cv.lbx
new file mode 100644
index 0000000000..76022c7447
--- /dev/null
+++ b/macros/latex/contrib/biblatex-contrib/biblatex-cv/american-cv.lbx
@@ -0,0 +1,53 @@
+\ProvidesFile{american-cv.lbx}[2020/03/11 v0.01 biblatex-cv localization (DES)]
+
+\InheritBibliographyExtras{american}
+\InheritBibliographyStrings{american}
+
+\NewBibliographyString{advisor, advisors, by, ci, committee, completed, concentration, contributed, convenor, doctoralthesis, externalexaminer, gpa, guestlecturer, inrevision, internalexaminer, invited, keynote, lecture, lectures, major, masters, mastersthesis, minor, month, months, numcites, ongoing, pending, phd, pi, postdoc, poster, presented, secondsupervisor, students, submitted, supervisor, talk, title, ta, undergraduate, undergraduatethesis, unsuccessful, with}
+
+\DeclareBibliographyStrings{%
+ advisor = {{advisor}{advisor}},
+ advisors = {{advisors}{advisors}},
+ by = {{by}{by}},
+ ci = {{co-investigator}{CI}},
+ committee = {{committee}{committee}},
+ completed = {{completed}{completed}},
+ concentration = {{concentration}{concentration}},
+ contributed = {{contributed}{contributed}},
+ convenor = {{module convenor}{module convenor}},
+ doctoralthesis = {{doctoral dissertation}{doctoral dissertation}},
+ externalexaminer = {{external examiner}{external examiner}},
+ gpa = {{GPA}{GPA}},
+ guestlecturer = {{guest lecturer}{guest lecturer}},
+ inrevision = {{in revision}{in revision}},
+ internalexaminer = {{internal examiner}{internal examiner}},
+ invited = {{invited}{invited}},
+ keynote = {{keynote}{keynote}},
+ lecture = {{lecture}{lecture}},
+ lectures = {{lectures}{lectures}},
+ major = {{major}{major}},
+ masters = {{masters student}{masters student}},
+ mastersthesis = {{masters thesis}{masters thesis}},
+ minor = {{minor}{minor}},
+ month = {{month}{month}},
+ months = {{months}{months}},
+ numcites = {{number of citations}{number of citations}},
+ ongoing = {{ongoing}{ongoing}},
+ pending = {{pending}{pending}},
+ phd = {{doctoral student}{doctoral student}},
+ pi = {{primary investigator}{PI}},
+ postdoc = {{postdoctoral fellow}{postdoctoral fellow}},
+ poster = {{poster}{poster}},
+ presented = {{presented}{presented}},
+ secondsupervisor = {{second supervisor}{second supervisor}},
+ students = {{students}{students}},
+ submitted = {{submitted}{submitted}},
+ supervisor = {{supervisor}{supervisor}},
+ talk = {{talk}{talk}},
+ title = {{title}{title}},
+ ta = {{teaching assistant}{teaching assistant}},
+ undergraduate = {{undergraduate student}{undergraduate student}},
+ undergraduatethesis = {{undergraduate thesis}{undergraduate thesis}},
+ unsuccessful = {{unsuccessful}{unsuccessful}},
+ with = {{with}{with}},
+}
diff --git a/macros/latex/contrib/biblatex-contrib/biblatex-cv/biblatex-cv.bbx b/macros/latex/contrib/biblatex-contrib/biblatex-cv/biblatex-cv.bbx
new file mode 100644
index 0000000000..aa1865367c
--- /dev/null
+++ b/macros/latex/contrib/biblatex-contrib/biblatex-cv/biblatex-cv.bbx
@@ -0,0 +1,736 @@
+\ProvidesFile{biblatex-cv.bbx}[2020/03/11 v0.01 biblatex-cv bibliography style (DES)]
+
+% The author year style seems better suited for a CV than the standard style
+\RequireBibliographyStyle{authoryear}
+
+\DeclareLanguageMappingSuffix{-cv}
+
+\RequirePackage{datenumber}
+\RequirePackage{fp}
+\RequirePackage{xpatch}
+
+% The verbose setting needs to be a style option because it influences the data model
+\newtoggle{cv@blx:verbose:committee}
+\newtoggle{cv@blx:verbose:education}
+\newtoggle{cv@blx:verbose:teaching}
+
+\DeclareBibliographyOption{verbose-lists}[true]{
+ \settoggle{cv@blx:verbose:committee}{#1}
+ \settoggle{cv@blx:verbose:education}{#1}
+ \settoggle{cv@blx:verbose:teaching}{#1}
+}
+\DeclareBibliographyOption{verbose-committee}[true]{
+ \settoggle{cv@blx:verbose:committee}{#1}
+}
+\DeclareBibliographyOption{verbose-education}[true]{
+ \settoggle{cv@blx:verbose:education}{#1}
+}
+\DeclareBibliographyOption{verbose-teaching}[true]{
+ \settoggle{cv@blx:verbose:teaching}{#1}
+}
+
+% Add new toggles for the bibliography drivers
+\newtoggle{cv@blx:pubstate}
+
+\newtoggle{cv@blx:education:advisor}
+\newtoggle{cv@blx:education:committee}
+\newtoggle{cv@blx:education:gpa}
+\newtoggle{cv@blx:education:honors}
+\newtoggle{cv@blx:education:major}
+\newtoggle{cv@blx:education:minor}
+\newtoggle{cv@blx:education:title}
+\newtoggle{cv@blx:funding:hidePI}
+\newtoggle{cv@blx:funding:role}
+\newtoggle{cv@blx:funding:status}
+\newtoggle{cv@blx:presentation:subtype}
+\newtoggle{cv@blx:presentation:type}
+\newtoggle{cv@blx:student:institution}
+\newtoggle{cv@blx:student:location}
+\newtoggle{cv@blx:student:department}
+\newtoggle{cv@blx:student:type}
+\newtoggle{cv@blx:school:years}
+\newtoggle{cv@blx:teaching:institution}
+\newtoggle{cv@blx:teaching:location}
+\newtoggle{cv@blx:teaching:department}
+
+% A shorthand way to patch the bibliography drivers defined in standard.bbx except for shorthand and set
+\newcommand{\cv@blx@drivers}{article, book, booklet, collection, inbook, incollection, inproceedings, manual, misc, online, patent, periodical, proceedings, report, thesis, unpublished}
+\newcommand{\cv@blx@do}[1]{}
+\newcommand{\cv@blx@patch@drivers}{%
+ \expandafter\forcsvlist\expandafter{%
+ \expandafter\cv@blx@do\expandafter%
+ }\expandafter{\cv@blx@drivers}%
+}
+
+% Define a macro to calculate the number of months between two dates assuming that the starting and ending years, months, and days are given.
+\newcounter{cv@blx@months@start}
+\newcounter{cv@blx@months@end}
+\newcommand{\cv@blx@months}{}
+\newcommand{\cv@blx@calculate@months}{%
+ \ifboolexpr{%
+ not test {\iffieldundef{endyear}} and%
+ not test {\iffieldundef{endmonth}} and%
+ not test {\iffieldundef{endday}}%
+ }{%
+ \setmydatenumber{cv@blx@months@end}{\thefield{endyear}}{\thefield{endmonth}}{\thefield{endday}}%
+ \setmydatenumber{cv@blx@months@start}{\thefield{year}}{\thefield{month}}{\thefield{day}}%
+ \FPsub\cv@blx@months{\thecv@blx@months@end}{\thecv@blx@months@start}%
+ \FPdiv\cv@blx@months{\cv@blx@months}{365.2425}%
+ \FPmul\cv@blx@months{\cv@blx@months}{12}%
+ \FPround\cv@blx@months{\cv@blx@months}{0}%
+ }{%
+ \undef{\cv@blx@months}%
+ }%
+}
+
+% Some of the bibliography drivers require new paragraphs within the bibliography entry. Some styles just use \par without the unspacing and punctuation tracking. In reading.bbx this is accomplished with \def\newblockpunct{\item}.
+\newrobustcmd*{\addpar}{\unspace\blx@postpunct\par\nobreak\blx@imc@resetpunctfont}
+%\newrobustcmd*{\addpar}{\unspace\blx@postpunct\item\blx@imc@resetpunctfont}
+
+% Make the pubstate field controllable with a toggle and include the journal title if present
+\xpatchbibmacro{addendum+pubstate}{%
+ \printfield{pubstate}%
+}{%
+ \iftoggle{cv@blx:pubstate}{%
+ \printfield{pubstate}%
+ \ifentrytype{unpublished}{%
+ \setunit*{\addcolon\addspace}%
+ \printfield{journaltitle}%
+ }{}%
+ }{}%
+}{}{}
+
+% The extradate part of the date label from the authoryear style is not needed in a CV
+\renewbibmacro*{date+extradate}{%
+ \iffieldundef{labelyear}{}{\printtext[parens]{\printdate}}%
+}
+
+% Provide bibliometric information at the end of the reference. All bibliography drivers defined in standard.bbx, except shorthand and set, end with:
+% \newunit\newblock
+% \usebibmacro{addendum+pubstate}%
+% \setunit{\bibpagerefpunct}\newblock
+% \usebibmacro{pageref}%
+% \newunit\newblock
+% \iftoggle{bbx:related}
+% {\usebibmacro{related:init}%
+% \usebibmacro{related}}
+% {}%
+% \usebibmacro{finentry}}
+% In reading.bbx the finentry bibmacro is patched, but it seems better to add the bibliometric information after the addendum+pubstate block and before the pageref and related blocks. The bibliometrics bibmacro also prints the note. For most drivers, the note is printed with \printfield{note}, but with the article driver it is printed with the note+pages bibmacro
+\renewcommand{\cv@blx@do}[1]{%
+ \xpatchbibdriver{#1}{%
+ \setunit{\bibpagerefpunct}\newblock%
+ }{%
+ \newunit\newblock%
+ \usebibmacro{bibliometrics}%
+ \setunit{\bibpagerefpunct}\newblock%
+ }{}{}%
+}
+\cv@blx@patch@drivers
+
+\renewcommand{\cv@blx@do}[1]{%
+ \xpatchbibdriver{#1}{%
+ \printfield{note}%
+ }{%
+ }{}{}%
+}
+\cv@blx@patch@drivers
+
+\xpatchbibdriver{article}{%
+ \usebibmacro{note+pages}%
+}{%
+ \setunit{\bibpagespunct}%
+ \printfield{pages}%
+ \newunit%
+}{}{}%
+
+% Define new bibmacros
+\newbibmacro{advisor}{%
+ \ifnameundef{advisor}{}{%
+ \ifnumgreater{\value{advisor}}{1}{%
+ \bibstring{advisors}%
+ }{%
+ \bibstring{advisor}%
+ }%
+ \addcolon\addspace%
+ \printnames[given-family]{advisor}%
+ }%
+}
+
+\newbibmacro{bibliometrics}{%
+ \ifboolexpr{%
+ ( test {\iffieldundef{numcites}} or test {\iffieldequalstr{numcites}{0}} ) and%
+ test {\iffieldundef{note}}%
+ }{}{%
+ \printtext[brackets]{%
+ \iffieldundef{numcites}{}{%
+ \iffieldequalstr{numcites}{0}{}{%
+ \bibstring{numCites}\addcolon\addnbspace\printfield{numcites}%
+ }%
+ }%
+ \newunit\setunit{\addcomma\addspace}%
+ \iffieldundef{note}{}{\printfield[sentencecase]{note}}%
+ }%
+ }%
+}
+
+\newbibmacro{class}{%
+ \mkdaterangelong{}%
+ \setunit{\addcomma\addspace}%
+ \printfield{role}%
+ \setunit{\addcomma\addspace}%
+ \iffieldundef{numlectures}{}{%
+ \iffieldequalstr{numlectures}{1}{%
+ \printfield{numlectures}\addnbspace\bibstring{lecture}%
+ }{%
+ \printfield{numlectures}\addnbspace\bibstring{lectures}%
+ }%
+ }%
+ \setunit{\addcomma\addspace}%
+ \iffieldundef{numstudents}{}{\printfield{numstudents}\addnbspace\bibstring{students}}%
+ \setunit{\adddot\addpar}\newblock%
+}
+
+\newbibmacro{classes}{%
+ \iffieldundef{classes}{%
+ \setunit{\adddot\addpar}\newblock%
+ \usebibmacro{class}%
+ }{%
+ \def\do##1{%
+ \entrydata*{##1}{%
+ \setunit{\adddot\addpar}\newblock%
+ \usebibmacro{class}%
+ }%
+ }%
+ \docsvfield{classes}%
+ }%
+}
+
+\newbibmacro{committee}{%
+ \ifnameundef{committee}{}{%
+ \bibstring{committee}\addcolon\addspace%
+ \printnames[given-family]{committee}%
+ }%
+}
+
+\newbibmacro{degrees}{%
+ \def\do##1{%
+ \entrydata*{##1}{%
+ \item\usedriver{}{degree}%
+ }%
+ }%
+ \list{}{\cv@blx@sublist}%
+ \docsvfield{degreelist}%
+ \endlist%
+}
+
+\newbibmacro{degree+major}{%
+ \printfield{degree}%
+ \iftoggle{cv@blx:education:major}{%
+ \ifboolexpr{%
+ test {\iffieldundef{degree}} or%
+ test {\iffieldundef{major}}%
+ }{}{\addspace\bibstring{in}\addspace}%
+ \printfield{major}%
+ }{}%
+}
+
+\newbibmacro{department+institution+location}{%
+ \iffieldundef{saveddepartment}{%
+ \printfield{department}%
+ \setunit{\addcomma\addspace}%
+ }{}%
+ \iflistundef{savedinstitution}{%
+ \printlist{institution}%
+ \setunit{\addcomma\addspace}%
+ }{}%
+ \iflistundef{savedlocation}{%
+ \printlist{location}%
+ }{}%
+}
+
+\newbibmacro{duration}{%
+ \ifundef{\cv@blx@months}{}{%
+ \printtext{\cv@blx@months\addnbspace}%
+ \ifnumgreater{\cv@blx@months}{1}{%
+ \bibstring{months}%
+ }{%
+ \bibstring{month}%
+ }%
+ }%
+}
+
+\newbibmacro{duration+amount+status}{%
+ \cv@blx@calculate@months%
+ \ifboolexpr{
+ test {\iffieldundef{amount}} and
+ test {\ifundef{\cv@blx@months}} and
+ (
+ test {\iffieldundef{status}} or
+ not test {\iftoggle{cv@blx:funding:status}} or
+ ) and
+ (
+ test {\iffieldundef{role}} or
+ not test {\iftoggle{cv@blx:funding:role}} or
+ (
+ test {\iftoggle{cv@blx:funding:hidePI}} and
+ test {\iffieldequalstr{role}{PI}}
+ )
+ )
+ }{}{%
+ \printtext[parens]{%
+ \usebibmacro{duration}%
+ \setunit{\addcomma\addspace}%
+ \printfield{amount}%
+ \setunit{\addcomma\addspace}%
+ \iftoggle{cv@blx:funding:status}{%
+ \printfield{status}%
+ \setunit{\addcomma\addspace}%
+ }{}%
+ \iftoggle{cv@blx:funding:role}{%
+ \iftoggle{cv@blx:funding:hidePI}{%
+ \iffieldequalstr{role}{PI}{\printfield{role}}{}%
+ }{%
+ \printfield{role}%
+ }%
+ }{}%
+ }%
+ }%
+}
+
+\newbibmacro{role}{%
+ \iffieldundef{role}{}{%
+ \printtext[brackets]{%
+ \printfield{role}%
+ }%
+ }%
+}
+
+% There is some sort of bug with the loop and the seasons not getting updated correctly
+\newbibmacro{semesters}{%
+ \iffieldundef{semesters}{%
+ \mkdaterangelong{}%
+ \setunit{\addspace}%
+ \iffieldundef{role}{}{\printtext[brackets]{\printfield{role}}}%
+ \setunit{\addcomma\addspace}%
+ }{%
+ \def\do##1{%
+ \entrydata*{##1}{%
+ \mkdaterangelong{}%
+ \setunit{\addspace}%
+ \iffieldundef{role}{}{%
+ \printtext[brackets]{\printfield{role}}%
+ }%
+ \setunit{\addcomma\addspace}%
+ }%
+ }%
+ \docsvfield{semesters}%
+ }%
+}
+
+\newbibmacro{type+presenter}{%
+ \ifboolexpr{
+ (
+ test {\iftoggle{cv@blx:presentation:type}} and
+ (
+ test {\iffieldequalstr{presentationtype}{keynote}} or
+ test {\iffieldequalstr{presentationtype}{invited}}
+ )
+ ) or (
+ test {\iftoggle{cv@blx:presentation:subtype}} and
+ not test {\iffieldundef{entrysubtype}}
+ ) or (
+ not test {\ifnameundef{presenter}}
+ )
+ }{%
+ \printtext[brackets]{%
+ \ifboolexpr{
+ test {\iftoggle{cv@blx:presentation:type}} and
+ (
+ test {\iffieldequalstr{presentationtype}{keynote}} or
+ test {\iffieldequalstr{presentationtype}{invited}}
+ )
+ }{%
+ \printfield[key]{presentationtype}%
+ \setunit{\addspace}%
+ }{}%
+ \iftoggle{cv@blx:presentation:subtype}{%
+ \printfield[key]{entrysubtype}%
+ \setunit{\addspace}%
+ }{}%
+ \ifnameundef{presenter}{}{%
+ \bibstring{presented}\addspace%
+ \printnames{presenter}%
+ }%
+ }%
+ }{}%
+}
+
+\newbibmacro{yearrange}{%
+ \printfield{year}%
+ \ifboolexpr{%
+ test {\iffieldundef{endyear}} or%
+ test {\iffieldsequal{year}{endyear}}%
+ }{}{\bibrangedash\printfield{endyear}}%
+}
+
+\newbibmacro{yearrange+dash}{%
+ \printfield{year}%
+ \iffieldundef{endyear}{%
+ \bibrangedash%
+ }{%
+ \iffieldsequal{year}{endyear}{}{\bibrangedash\printfield{endyear}}%
+ }%
+}
+
+% The biblatex-cv style allows for custom entry types that do not have bibliography drivers defined in standard.bbx.
+
+% ABSTRACT
+% The abstract bibliography driver is a modified version of the article bibliography driver.
+\csletcs{blx@bbx@abstract}{blx@bbx@article}
+\DeclareFieldFormat[abstract]{citetitle}{\mkbibquote{#1\isdot}}
+\DeclareFieldFormat[abstract]{number}{#1}
+\DeclareFieldFormat[abstract]{series}{%
+ \ifinteger{#1}{%
+ \mkbibordseries{#1}~\bibstring{jourser}%
+ }{%
+ \ifbibstring{#1}{\bibstring{#1}}{#1}%
+ }%
+}
+\DeclareFieldFormat[abstract]{title}{\mkbibquote{#1\isdot}}
+\DeclareFieldFormat[abstract]{volume}{#1}
+\xpatchbibdriver{abstract}{\usebibmacro{doi+eprint+url}}{%
+ \usebibmacro{type+presenter}%
+ \newunit\newblock%
+ \usebibmacro{doi+eprint+url}%
+}{}{}
+
+% COMMITTEE
+\DeclareBibliographyDriver{committee}{%
+ \usebibmacro{bibindex}%
+ \usebibmacro{begentry}%
+ \usebibmacro{semesters}%
+ \printfield{title}%
+ \setunit{\addcomma\addspace}%
+ \usebibmacro{department+institution+location}%
+ \newunit\newblock%
+ \usebibmacro{doi+eprint+url}%
+ \newunit\newblock%
+ \usebibmacro{addendum+pubstate}%
+ \newunit\newblock%
+ \usebibmacro{bibliometrics}%
+ \setunit{\bibpagerefpunct}\newblock%
+ \usebibmacro{pageref}%
+ \newunit\newblock%
+ \iftoggle{bbx:related}{%
+ \usebibmacro{related:init}%
+ \usebibmacro{related}%
+ }{}%
+ \usebibmacro{finentry}%
+}
+
+% EDUCATION
+\DeclareBibliographyDriver{school}{%
+ \usebibmacro{bibindex}%
+ \usebibmacro{begentry}%
+ \usebibmacro{department+institution+location}%
+ \setunit{\addcomma\addspace}%
+ \iftoggle{cv@blx:school:years}{%
+ \usebibmacro{yearrange}%
+ }{}%
+ \newblockpunct{\addperiod\addpar}%
+ \iftoggle{cv@blx:education:gpa}{%
+ \iffieldundef{gpa}{}{%
+ \printfield{gpa}%
+ \newblockpunct{\addpar}%
+ }%
+ }{}%
+ \iftoggle{cv@blx:education:honors}{%
+ \iflistundef{honors}{}{%
+ \bibstring{honors}\addcolon\addspace%
+ \printlist{honors}%
+ }
+ \newblockpunct{\addpar}%
+ }{}%
+ \usebibmacro{degrees}%
+ \usebibmacro{doi+eprint+url}%
+ \newunit\newblock%
+ \usebibmacro{addendum+pubstate}%
+ \newunit\newblock%
+ \usebibmacro{bibliometrics}%
+ \setunit{\bibpagerefpunct}\newblock%
+ \usebibmacro{pageref}%
+ \newunit\newblock%
+ \iftoggle{bbx:related}{%
+ \usebibmacro{related:init}%
+ \usebibmacro{related}%
+ }{}%
+ \begingroup%
+ \renewcommand{\finentrypunct}{}%
+ \usebibmacro{finentry}%
+ \endgroup%
+}
+
+\DeclareBibliographyDriver{degree}{%
+ \usebibmacro{bibindex}%
+ \usebibmacro{begentry}%
+ \usebibmacro{degree+major}%
+ \setunit{\addcomma\addspace}%
+ \usebibmacro{department+institution+location}%
+ \setunit{\addcomma\addspace}%
+ \usebibmacro{yearrange}%
+ \newblockpunct{\addperiod\addpar}%
+ \iftoggle{cv@blx:education:title}{%
+ \printfield{title}%
+ \newblockpunct{\addpar}%
+ }{}%
+ \iftoggle{cv@blx:education:advisor}{%
+ \usebibmacro{advisor}%
+ \newblockpunct{\addpar}%
+ }{}%
+ \iftoggle{cv@blx:education:committee}{%
+ \usebibmacro{committee}%
+ \newblockpunct{\addpar}%
+ }{}%
+ \iftoggle{cv@blx:education:minor}{%
+ \iflistundef{minor}{}{%
+ \bibstring{minor}\addcolon\addspace%
+ \printlist{minor}%
+ }%
+ \newblockpunct{\addpar}%
+ \iflistundef{concentration}{}{%
+ \bibstring{concentration}\addcolon\addspace%
+ \printlist{concentration}%
+ }%
+ \newblockpunct{\addpar}%
+ }{}%
+ \iftoggle{cv@blx:education:gpa}{%
+ \printfield{gpa}%
+ \newblockpunct{\addpar}%
+ }{}%
+ \iftoggle{cv@blx:education:honors}{%
+ \iflistundef{honors}{}{%
+ \printlist{honors}%
+ }
+ \newblockpunct{\addpar}%
+ }{}%
+ \usebibmacro{doi+eprint+url}%
+ \newunit\newblock%
+ \usebibmacro{addendum+pubstate}%
+ \newunit\newblock%
+ \usebibmacro{bibliometrics}%
+ \setunit{\bibpagerefpunct}\newblock%
+ \usebibmacro{pageref}%
+ \newunit\newblock%
+ \iftoggle{bbx:related}{%
+ \usebibmacro{related:init}%
+ \usebibmacro{related}%
+ }{}%
+ \begingroup%
+ \renewcommand{\finentrypunct}{}%
+ \usebibmacro{finentry}%
+ \endgroup%
+}
+
+% FUNDING
+\DeclareBibliographyDriver{funding}{%
+ \usebibmacro{bibindex}%
+ \usebibmacro{begentry}%
+ \usebibmacro{yearrange}%
+ \setunit{\addspace}%
+ \usebibmacro{duration+amount+status}%
+ \setunit{\addcolon\addspace}%
+ \printfield{funder}%
+ \setunit{\addcomma\addspace}%
+ \printfield{type}%
+ \setunit{\addspace}%
+ \printfield[parens]{number}%
+ \newunit\newblock%
+ \usebibmacro{title}%
+ \newunit\newblock%
+ \usebibmacro{doi+eprint+url}%
+ \newunit\newblock%
+ \usebibmacro{addendum+pubstate}%
+ \newunit\newblock%
+ \usebibmacro{bibliometrics}%
+ \setunit{\bibpagerefpunct}\newblock%
+ \usebibmacro{pageref}%
+ \newunit\newblock%
+ \iftoggle{bbx:related}{%
+ \usebibmacro{related:init}%
+ \usebibmacro{related}%
+ }{}%
+ \usebibmacro{finentry}%
+}
+
+% PRESENTATION
+\DeclareBibliographyDriver{presentation}{%
+ \usebibmacro{bibindex}%
+ \usebibmacro{begentry}%
+ \usebibmacro{author}%
+ \setunit{\printdelim{nametitledelim}}\newblock%
+ \usebibmacro{title}%
+ \setunit*{\adddot\addspace}%
+ \usebibmacro{department+institution+location}%
+ \newunit\newblock%
+ \usebibmacro{type+presenter}%
+ \newunit\newblock%
+ \usebibmacro{doi+eprint+url}%
+ \newunit\newblock%
+ \usebibmacro{addendum+pubstate}%
+ \newunit\newblock%
+ \usebibmacro{bibliometrics}%
+ \setunit{\bibpagerefpunct}\newblock%
+ \usebibmacro{pageref}%
+ \newunit\newblock%
+ \iftoggle{bbx:related}{%
+ \usebibmacro{related:init}%
+ \usebibmacro{related}%
+ }{}%
+ \usebibmacro{finentry}%
+}
+
+% STUDENT
+\DeclareBibliographyDriver{student}{%
+ \usebibmacro{bibindex}%
+ \usebibmacro{begentry}%
+ \printnames{name}%
+ \setunit{\addcomma\addspace}%
+ \iftoggle{cv@blx:student:type}{%
+ \printfield{entrysubtype}%
+ \setunit{\addcomma\addspace}%
+ }{}%
+ \usebibmacro{yearrange}%
+ \setunit{\addcomma\addspace}%
+ \iftoggle{cv@blx:student:department}{%
+ \printfield{department}%
+ \setunit{\addcomma\addspace}%
+ }{}%
+ \iftoggle{cv@blx:student:institution}{%
+ \printlist{institution}%
+ \setunit{\addcomma\addspace}%
+ }{}%
+ \iftoggle{cv@blx:student:location}{%
+ \printlist{location}%
+ \setunit{\addcomma\addspace}%
+ }{}%
+ \newunit\newblock%
+ \usebibmacro{role}%
+ \newunit\newblock%
+ \usebibmacro{doi+eprint+url}%
+ \newunit\newblock%
+ \usebibmacro{addendum+pubstate}%
+ \newunit\newblock%
+ \usebibmacro{bibliometrics}%
+ \setunit{\bibpagerefpunct}\newblock%
+ \usebibmacro{pageref}%
+ \newunit\newblock%
+ \iftoggle{bbx:related}{%
+ \usebibmacro{related:init}%
+ \usebibmacro{related}%
+ }{}%
+ \usebibmacro{finentry}%
+}
+
+% TEACHING
+\DeclareBibliographyDriver{teaching}{%
+ \usebibmacro{bibindex}%
+ \usebibmacro{begentry}%
+ \printfield{title}%
+ \setunit{\addspace}%
+ \printfield[parens]{number}%
+ \setunit{\addcomma\addspace}%
+ \iftoggle{cv@blx:teaching:department}{%
+ \printfield{department}%
+ \setunit{\addcomma\addspace}%
+ }{}%
+ \iftoggle{cv@blx:teaching:institution}{%
+ \printlist{institution}%
+ \setunit{\addcomma\addspace}%
+ }{}%
+ \iftoggle{cv@blx:teaching:location}{%
+ \printlist{location}%
+ \setunit{\addcomma\addspace}%
+ }{}%
+ \newunit\newblock%
+ \usebibmacro{doi+eprint+url}%
+ \newunit\newblock%
+ \usebibmacro{addendum+pubstate}%
+ \newunit\newblock%
+ \usebibmacro{bibliometrics}%
+ \setunit{\bibpagerefpunct}\newblock%
+ \usebibmacro{pageref}%
+ \newunit\newblock%
+ \iftoggle{cv@blx:verbose:teaching}{%
+ \usebibmacro{classes}%
+ }{%
+ \setunit{\adddot\addspace}\newblock%
+ \usebibmacro{class}%
+ }%
+ \iftoggle{bbx:related}{%
+ \usebibmacro{related:init}%
+ \usebibmacro{related}%
+ }{}%
+ \usebibmacro{finentry}%
+}
+
+% Define new field formats
+
+% Allow key datatypes to access localization strings
+\DeclareFieldFormat{key}{\ifbibstring{#1}{\bibstring{#1}}{#1}}
+\DeclareFieldFormat{role}{\ifbibstring{#1}{\bibstring{#1}}{#1}}
+\DeclareFieldFormat{status}{\ifbibstring{#1}{\bibstring{#1}}{#1}}
+
+\DeclareFieldFormat[committee]{title}{#1}
+
+\DeclareFieldFormat[degree]{degree}{#1\isdot}
+\DeclareFieldFormat[degree]{title}{\bibstring{title}\addcolon\addspace#1}
+\DeclareFieldFormat[school, degree]{gpa}{\bibstring{gpa}\addcolon\addspace#1}
+
+\DeclareFieldFormat[funding]{amount}{\printfield{currency}#1}
+
+\DeclareFieldFormat[teaching]{title}{#1}
+
+\DeclareFieldFormat[student]{entrysubtype}{\ifbibstring{#1}{\bibstring{#1}}{#1}}
+
+\DeclareFieldFormat[course]{related}{\mkbibparens{#1}}
+
+% The date label from the authoryear style includes "no date" which is not needed in a CV
+\DeclareLabeldate{%
+ \field{date}
+ \field{year}
+ \field{eventdate}
+ \field{origdate}
+ \field{urldate}
+}
+
+% Redefine ydnt sorting to sort by endyear. Taken from biblatex.def
+\DeclareSortingTemplate{ydnt}{
+ \sort{
+ \field{presort}
+ }
+ \sort[final]{
+ \field{sortkey}
+ }
+ \sort[final, direction=descending]{
+ \field{sortyear}
+ \field[padwidth = 4, padchar = 9]{endyear}
+ }
+ \sort[direction=descending]{
+ \field{sortyear}
+ \field{year}
+ \literal{9999}
+ }
+ \sort{
+ \field{sortname}
+ \field{author}
+ \field{editor}
+ \field{translator}
+ \field{sorttitle}
+ \field{title}
+ }
+ \sort{
+ \field{sorttitle}
+ \field{title}
+ }
+}
diff --git a/macros/latex/contrib/biblatex-contrib/biblatex-cv/biblatex-cv.cbx b/macros/latex/contrib/biblatex-contrib/biblatex-cv/biblatex-cv.cbx
new file mode 100644
index 0000000000..80cff4e9ff
--- /dev/null
+++ b/macros/latex/contrib/biblatex-contrib/biblatex-cv/biblatex-cv.cbx
@@ -0,0 +1,3 @@
+\ProvidesFile{biblatex-cv.cbx}[2020/03/11 v0.01 biblatex-cv citation style (DES)]
+
+\RequireCitationStyle{authoryear}
diff --git a/macros/latex/contrib/biblatex-contrib/biblatex-cv/biblatex-cv.dbx b/macros/latex/contrib/biblatex-contrib/biblatex-cv/biblatex-cv.dbx
new file mode 100644
index 0000000000..745ab411bc
--- /dev/null
+++ b/macros/latex/contrib/biblatex-contrib/biblatex-cv/biblatex-cv.dbx
@@ -0,0 +1,386 @@
+\ProvidesFile{biblatex-cv.dbx}[2020/03/11 v0.01 biblatex-cv data model (DES)]
+
+% Add new data fields for both existing and new entry types
+\DeclareDatamodelFields[type=list, datatype=name]{advisor, committee, name, presenter}
+\DeclareDatamodelFields[type=list, datatype=literal]{concentration, honors, minor}
+\DeclareDatamodelFields[type=field, datatype=integer]{amount, numcites, numlectures, numstudents}
+\DeclareDatamodelFields[type=field, datatype=literal]{currency, degree, department, funder, gpa, major, presentationtype}
+\DeclareDatamodelFields[type=field, datatype=key]{role, status}
+\DeclareDatamodelFields[type=field, format=xsv, datatype=entrykey]{classes, degreelist, semesters}
+
+% For all name lists, create a new field to indicate if a name has been deleted from the list
+\newcommand{\cv@blx@tempa}[1]{%
+ \newtoggle{cv@blx:delete:#1}%
+ \DeclareDatamodelFields[type=field, datatype=integer]{#1deleted}%
+}
+\forcsvlist{\cv@blx@tempa}{afterword, annotator, author, bookauthor, %
+ commentator, editor, editora, editorb, editorc, foreword, holder, %
+ introduction, namea, nameb, namec, translator, shortauthor, %
+ shorteditor, advisor, committee, name, presenter%
+}
+
+% Modify the existing data model inherited from blx-dm.def
+\DeclareDatamodelConstraints{
+ \constraint[type=data, datatype=pattern, pattern=\regexp{(
+ ^forthcoming$|
+ ^inpreparation$|
+ ^inpress$|
+ ^inrevision$|
+ ^prepublished$|
+ ^submitted$
+ )}]{
+ \constraintfield{pubstate}
+ }
+}
+
+% Define global changes to the data model
+\DeclareDatamodelConstraints{
+ \constraint[type=data, datatype=pattern,
+ pattern=\regexp{(
+ ^contributed$|
+ ^internal$|
+ ^invited$|
+ ^job$|
+ ^keynote$
+ )}
+ ]{
+ \constraintfield{presentationtype}
+ }
+}
+
+% ARTICLE
+\DeclareDatamodelEntryfields[article]{numcites}
+\DeclareDatamodelConstraints[article]{
+ \constraint[type=mandatory]{
+ \constraintfield{numcites}
+ }
+}
+
+% UNPUBLISHED
+\DeclareDatamodelEntryfields[unpublished]{journaltitle}
+
+% THESIS
+\DeclareDatamodelConstraints[thesis]{
+ \constraint[type=data, datatype=pattern,
+ pattern=\regexp{(
+ ^doctoralthesis$|
+ ^mastersthesis$|
+ ^undergradthesis$
+ )}%
+ ]{
+ \constraintfield{type}
+ }
+}
+
+% Define the data model for new entry types not defined in blx-dm.def
+
+% ABSTRACT
+% The data model for the abstract entrytype is a modified version of the article entrytype data model. We start by copying the article data model from blx-dm.def
+\DeclareDatamodelEntrytypes{abstract}
+\DeclareDatamodelEntryfields[abstract]{
+ addendum,
+ annotator,
+ author,
+ commentator,
+ doi,
+ editor,
+ editora,
+ editorb,
+ editorc,
+ editortype,
+ editoratype,
+ editorbtype,
+ editorctype,
+ eid,
+ eprint,
+ eprintclass,
+ eprinttype,
+ issn,
+ issue,
+ issuetitle,
+ issuesubtitle,
+ journalsubtitle,
+ journaltitle,
+ language,
+ note,
+ number,
+ origlanguage,
+ pages,
+ pubstate,
+ series,
+ subtitle,
+ title,
+ titleaddon,
+ translator,
+ version,
+ volume,
+}
+\DeclareDatamodelConstraints[abstract]{
+ \constraint[type=mandatory]{
+ \constraintfieldsxor{
+ \constraintfield{date}
+ \constraintfield{year}
+ }
+ \constraintfield{author}
+ \constraintfield{journaltitle}
+ \constraintfield{title}
+ }
+}
+
+\DeclareDatamodelEntryfields[abstract]{
+ entrysubtype,
+ presentationtype,
+ presenter,
+ presenterdeleted,
+}
+\DeclareDatamodelConstraints[abstract]{
+ \constraint[type=mandatory]{
+ \constraintfield{entrysubtype}
+ \constraintfield{presentationtype}
+ \constraintfieldsor{
+ \constraintfield{presenter}
+ \constraintfield{presenterdeleted}
+ }
+ }
+ \constraint[type=data, datatype=pattern,
+ pattern=\regexp{(^poster$|^talk$|)}
+ ]{
+ \constraintfield{entrysubtype}
+ }
+}
+
+% COMMITTEE
+\DeclareDatamodelEntrytypes{committee}
+\DeclareDatamodelEntryfields[committee]{
+ date,day,endday,endmonth,endyear,month,year,
+ entrysubtype,
+ title,
+ institution,
+ location,
+ department,
+ role,
+ semesters,
+}
+\DeclareDatamodelConstraints[committee]{
+ \constraint[type=mandatory]{
+ \constraintfieldsor{
+ \constraintfield{date}
+ \constraintfield{semesters}
+ }
+ \constraintfield{entrysubtype}
+ \constraintfield{title}
+ \constraintfield{institution}
+ }
+ \constraint[type=data, datatype=pattern, pattern=\regexp{(%
+ ^departmental$|
+ ^external$|
+ ^institutional$
+ )}]{
+ \constraintfield{entrysubtype}
+ }
+}
+
+% EDUCATION
+\DeclareDatamodelEntrytypes{school}
+\DeclareDatamodelEntryfields[school]{
+ date,day,endday,endmonth,endyear,month,year,
+ institution,
+ location,
+ department,
+ degreelist,
+ gpa,
+ honors,
+}
+\DeclareDatamodelConstraints[school]{
+ \constraint[type=mandatory]{
+ \constraintfieldsxor{
+ \constraintfield{date}
+ \constraintfield{year}
+ }
+ \constraintfield{institution}
+ \constraintfield{location}
+ }
+}
+
+\DeclareDatamodelEntrytypes{degree}
+\DeclareDatamodelEntryfields[degree]{
+ date,day,endday,endmonth,endyear,month,year,
+ institution,
+ location,
+ department,
+ degree,
+ gpa,
+ honors,
+ major,
+ minor,
+ concentration,
+ advisor,
+ committee,
+ title,
+}
+\DeclareDatamodelConstraints[degree]{
+ \constraint[type=mandatory]{
+ \constraintfieldsxor{
+ \constraintfield{date}
+ \constraintfield{year}
+ }
+ \constraintfield{institution}
+ \constraintfield{location}
+ }
+}
+
+% FUNDING
+\DeclareDatamodelEntrytypes{funding}
+\DeclareDatamodelEntryfields[funding]{
+ date,day,endday,endmonth,endyear,month,year,
+ entrysubtype,
+ status,
+ author,
+ funder,
+ title,
+ role,
+ amount,
+ currency,
+ number,
+ type,
+}
+\DeclareDatamodelConstraints[funding]{
+ \constraint[type=mandatory]{
+ \constraintfieldsxor{
+ \constraintfield{date}
+ \constraintfield{year}
+ }
+ \constraintfield{entrysubtype}
+ \constraintfield{status}
+ \constraintfield{author}
+ \constraintfield{funder}
+ }
+ \constraint[type=data, datatype=pattern, pattern=\regexp{(^individual$|^institutional$)}]{
+ \constraintfield{entrysubtype}
+ }
+ \constraint[
+ type=data, datatype=pattern,
+ pattern=\regexp{(^completed$|^ongoing$|^pending$|^submitted$|^unsuccessful$)}
+ ]{
+ \constraintfield{status}
+ }
+}
+
+% PRESENTATION
+\DeclareDatamodelEntrytypes{presentation}
+\DeclareDatamodelEntryfields[presentation]{
+ date,day,endday,endmonth,endyear,month,year,
+ entrysubtype,
+ author,
+ presentationtype,
+ presenter,
+ presenterdeleted,
+ title,
+ institution,
+ location,
+ department,
+}
+\DeclareDatamodelConstraints[presentation]{
+ \constraint[type=mandatory]{
+ \constraintfieldsxor{
+ \constraintfield{date}
+ \constraintfield{year}
+ }
+ \constraintfield{entrysubtype}
+ \constraintfield{author}
+ \constraintfield{presentationtype}
+ \constraintfieldsor{
+ \constraintfield{presenter}
+ \constraintfield{presenterdeleted}
+ }
+ \constraintfield{title}
+ \constraintfield{institution}
+ \constraintfield{location}
+ }
+ \constraint[type=data, datatype=pattern,
+ pattern=\regexp{(^poster$|^talk$|)}
+ ]{
+ \constraintfield{entrysubtype}
+ }
+}
+
+% STUDENT
+\DeclareDatamodelEntrytypes{student}
+\DeclareDatamodelEntryfields[student]{
+ date,day,endday,endmonth,endyear,month,year,
+ entrysubtype,
+ role,
+ name,
+ institution,
+ location,
+ department,
+}
+\DeclareDatamodelConstraints[student]{
+ \constraint[type=mandatory]{
+ \constraintfieldsxor{
+ \constraintfield{date}
+ \constraintfield{year}
+ }
+ \constraintfield{entrysubtype}
+ \constraintfield{role}
+ \constraintfield{name}
+ \constraintfield{institution}
+ \constraintfield{location}
+ }
+ \constraint[type=data, datatype=pattern, pattern=\regexp{(^masters$|^phd$|^postdoc$|^undergraduate$)}]{
+ \constraintfield{entrysubtype}
+ }
+ \constraint[type=data, datatype=pattern, pattern=\regexp{(^externalexaminer$|^internalexaminer$|^secondsupervisor$|^supervisor$)}]{
+ \constraintfield{role}
+ }
+}
+
+% TEACHING
+\DeclareDatamodelEntrytypes{teaching}
+\DeclareDatamodelEntryfields[teaching]{
+ date,day,endday,endmonth,endyear,month,year,
+ entrysubtype,
+ title,
+ department,
+ institution,
+ location,
+ number,
+ numlectures,
+ numstudents,
+ role,
+ classes,
+}
+\DeclareDatamodelConstraints[teaching]{
+ \constraint[type=mandatory]{
+ \constraintfieldsxor{
+ \constraintfield{date}
+ \constraintfield{classes}
+ }
+ \constraintfield{entrysubtype}
+ \constraintfield{title}
+ \constraintfield{department}
+ \constraintfield{institution}
+ \constraintfield{location}
+ }
+ \constraint[type=data, datatype=pattern, pattern=\regexp{(^graduate$|^undergraduate$)}]{
+ \constraintfield{entrysubtype}
+ }
+}
+
+% ADDITIONAL FIELDS TO ADD TO SUPPORTED ENTRY TYPES
+% Education: description, coursework
+% Funding: co-investigators, description, publications
+% Student: title, funding, current position, description, publications, something to indicate students that dropped out
+% Teaching: description, textbook, objectives, rating
+
+% ADDITIONAL ENTRY TYPES NOT YET SUPPORTED:
+% Panels Organized -- Maybe this should be a subtype of \bibtype{committee}
+% Panel Discussant -- Maybe this should be a subtype of \bibtype{committee}
+% Awards/Honors
+% Positions -- permanent, visiting, adjunct, affiliated, ...
+% Research Experience
+% Media Coverage
+% Related skills
+% Non-Academic Work
+% Professional Memberships/Affiliations
+% References
diff --git a/macros/latex/contrib/biblatex-contrib/biblatex-cv/biblatex-cv.sty b/macros/latex/contrib/biblatex-contrib/biblatex-cv/biblatex-cv.sty
new file mode 100644
index 0000000000..834d7724aa
--- /dev/null
+++ b/macros/latex/contrib/biblatex-contrib/biblatex-cv/biblatex-cv.sty
@@ -0,0 +1,776 @@
+% Copyright (c) 2020- Daniel E. Shub.
+%
+% Permission is granted to copy, distribute and/or modify this software under the terms of the LaTeX Project Public License (LPPL), version 1.3c.
+%
+% This software is provided 'as is', without warranty of any kind, either expressed or implied, including, but not limited to, the implied warranties of merchantability and fitness for a particular purpose.
+
+\NeedsTeXFormat{LaTeX2e}[2005/12/01]
+\ProvidesPackage{biblatex-cv}[2020/03/11 v0.01 biblatex-cv bibliography style (DES)]
+
+\DeclareOption*{\PassOptionsToPackage{\CurrentOption}{biblatex}}
+\ProcessOptions\relax
+
+\PassOptionsToPackage{
+ labelnumber = true, defernumbers = true, backend = biber,
+ style = biblatex-cv, sorting = ydnt, maxnames = 999
+}{biblatex}
+\RequirePackage{biblatex}
+
+\RequirePackage{expl3, xparse}
+\RequirePackage{totcount}
+\RequirePackage{xpatch}
+
+% Document layout
+\setlength{\biblabelsep}{0.5em}
+\defbibheading{bibliography}{}
+
+\toggletrue{cv@blx:delete:author}
+\toggletrue{cv@blx:delete:presenter}
+
+\toggletrue{cv@blx:pubstate}
+
+\togglefalse{cv@blx:funding:hidePI}
+\toggletrue{cv@blx:funding:role}
+\toggletrue{cv@blx:funding:status}
+\toggletrue{cv@blx:presentation:subtype}
+\toggletrue{cv@blx:presentation:type}
+\toggletrue{cv@blx:student:institution}
+\toggletrue{cv@blx:student:location}
+\toggletrue{cv@blx:student:department}
+\toggletrue{cv@blx:student:type}
+\toggletrue{cv@blx:school:years}
+\toggletrue{cv@blx:teaching:institution}
+\toggletrue{cv@blx:teaching:location}
+\toggletrue{cv@blx:teaching:department}
+
+% Bibfilters
+
+% ABSTRACT
+\defbibcheck{Abstract}{%
+ \ifentrytype{abstract}{}{\skipentry}%
+}
+\defbibcheck{Abstract:Contributed}{%
+ \ifentrytype{abstract}{}{\skipentry}%
+ \iffieldequalstr{presentationtype}{contributed}{}{\skipentry}%
+}
+\defbibcheck{Abstract:Invited}{%
+ \ifentrytype{abstract}{}{\skipentry}%
+ \iffieldequalstr{presentationtype}{invited}{}{\skipentry}%
+}
+\defbibcheck{Abstract:Keynote}{%
+ \ifentrytype{abstract}{}{\skipentry}%
+ \iffieldequalstr{presentationtype}{keynote}{}{\skipentry}
+}
+\defbibcheck{Abstract:NotInternalOrJob}{%
+ \ifentrytype{abstract}{}{\skipentry}%
+ \iffieldequalstr{presentationtype}{internal}{\skipentry}{%
+ \iffieldequalstr{presentationtype}{job}{\skipentry}{}%
+ }%
+}
+
+\defbibcheck{Abstract:PosterContributed}{%
+ \ifentrytype{abstract}{}{\skipentry}%
+ \iffieldequalstr{entrysubtype}{poster}{}{\skipentry}%
+ \iffieldequalstr{presentationtype}{contributed}{}{\skipentry}%
+}
+\defbibcheck{Abstract:PosterInvited}{%
+ \ifentrytype{abstract}{}{\skipentry}%
+ \iffieldequalstr{entrysubtype}{poster}{}{\skipentry}%
+ \iffieldequalstr{presentationtype}{invited}{}{\skipentry}%
+}
+\defbibcheck{Abstract:PosterKeynote}{%
+ \ifentrytype{abstract}{}{\skipentry}%
+ \iffieldequalstr{entrysubtype}{poster}{}{\skipentry}%
+ \iffieldequalstr{presentationtype}{keynote}{}{\skipentry}
+}
+\defbibcheck{Abstract:PosterNotInternalOrJob}{%
+ \ifentrytype{abstract}{}{\skipentry}%
+ \iffieldequalstr{entrysubtype}{poster}{}{\skipentry}%
+ \iffieldequalstr{presentationtype}{internal}{\skipentry}{%
+ \iffieldequalstr{presentationtype}{job}{\skipentry}{}%
+ }%
+}
+
+\defbibcheck{Abstract:TalkContributed}{%
+ \ifentrytype{abstract}{}{\skipentry}%
+ \iffieldequalstr{entrysubtype}{talk}{}{\skipentry}%
+ \iffieldequalstr{presentationtype}{contributed}{}{\skipentry}%
+}
+\defbibcheck{Abstract:TalkInvited}{%
+ \ifentrytype{abstract}{}{\skipentry}%
+ \iffieldequalstr{entrysubtype}{talk}{}{\skipentry}%
+ \iffieldequalstr{presentationtype}{invited}{}{\skipentry}%
+}
+\defbibcheck{Abstract:TalkKeynote}{%
+ \ifentrytype{abstract}{}{\skipentry}%
+ \iffieldequalstr{entrysubtype}{talk}{}{\skipentry}%
+ \iffieldequalstr{presentationtype}{keynote}{}{\skipentry}
+}
+\defbibcheck{Abstract:TalkNotInternalOrJob}{%
+ \ifentrytype{abstract}{}{\skipentry}%
+ \iffieldequalstr{entrysubtype}{talk}{}{\skipentry}%
+ \iffieldequalstr{presentationtype}{internal}{\skipentry}{%
+ \iffieldequalstr{presentationtype}{job}{\skipentry}{}%
+ }%
+}
+
+% COMMITTEE
+\defbibcheck{Committee}{%
+ \ifentrytype{committee}{}{\skipentry}%
+}
+\defbibcheck{Committee:External}{%
+ \ifentrytype{committee}{}{\skipentry}%
+ \iffieldequalstr{entrysubtype}{external}{}{\skipentry}%
+}
+\defbibcheck{Committee:Departmental}{%
+ \ifentrytype{committee}{}{\skipentry}%
+ \iffieldequalstr{entrysubtype}{departmental}{}{\skipentry}%
+}
+\defbibcheck{Committee:Institutional}{%
+ \ifentrytype{committee}{}{\skipentry}%
+ \iffieldequalstr{entrysubtype}{institutional}{}{\skipentry}%
+}
+\defbibcheck{Committee:NotExternal}{%
+ \ifentrytype{committee}{}{\skipentry}%
+ \iffieldequalstr{entrysubtype}{external}{\skipentry}{}%
+}
+
+% EDUCATION
+\defbibcheck{Education}{%
+ \ifentrytype{school}{}{%
+ \ifentrytype{degree}{}{\skipentry}%
+ }%
+}
+
+% FUNDING
+\defbibcheck{Funding}{%
+ \ifentrytype{funding}{}{\skipentry}%
+}
+\defbibcheck{Funding:Completed}{%
+ \ifentrytype{funding}{}{\skipentry}%
+ \iffieldequalstr{status}{completed}{}{\skipentry}%
+}
+\defbibcheck{Funding:Ongoing}{%
+ \ifentrytype{funding}{}{\skipentry}%
+ \iffieldequalstr{status}{ongoing}{}{\skipentry}%
+}
+\defbibcheck{Funding:Pending}{%
+ \ifentrytype{funding}{}{\skipentry}%
+ \iffieldequalstr{status}{pending}{}{\skipentry}%
+}
+\defbibcheck{Funding:Submitted}{%
+ \ifentrytype{funding}{}{\skipentry}%
+ \iffieldequalstr{status}{submitted}{}{\skipentry}%
+}
+\defbibcheck{Funding:Unsuccessful}{%
+ \ifentrytype{funding}{}{\skipentry}%
+ \iffieldequalstr{status}{unsuccessful}{}{\skipentry}%
+}
+\defbibcheck{Funding:CurrentAndPending}{%
+ \ifentrytype{funding}{}{\skipentry}%
+ \iffieldequalstr{status}{ongoing}{}{%
+ \iffieldequalstr{status}{pending}{}{%
+ \iffieldequalstr{status}{submitted}{}{\skipentry}%
+ }%
+ }%
+}
+\defbibcheck{Funding:NotUnsuccessful}{%
+ \ifentrytype{funding}{}{\skipentry}%
+ \iffieldequalstr{status}{unsuccessful}{\skipentry}{}%
+}
+
+\defbibcheck{Funding:Individual}{%
+ \ifentrytype{funding}{}{\skipentry}%
+ \iffieldequalstr{entrysubtype}{individual}{}{\skipentry}%
+}
+\defbibcheck{Funding:IndividualCompleted}{%
+ \ifentrytype{funding}{}{\skipentry}%
+ \iffieldequalstr{entrysubtype}{individual}{}{\skipentry}%
+ \iffieldequalstr{status}{completed}{}{\skipentry}%
+}
+\defbibcheck{Funding:IndividualOngoing}{%
+ \ifentrytype{funding}{}{\skipentry}%
+ \iffieldequalstr{entrysubtype}{individual}{}{\skipentry}%
+ \iffieldequalstr{status}{ongoing}{}{\skipentry}%
+}
+\defbibcheck{Funding:IndividualPending}{%
+ \ifentrytype{funding}{}{\skipentry}%
+ \iffieldequalstr{entrysubtype}{individual}{}{\skipentry}%
+ \iffieldequalstr{status}{pending}{}{\skipentry}%
+}
+\defbibcheck{Funding:IndividualSubmitted}{%
+ \ifentrytype{funding}{}{\skipentry}%
+ \iffieldequalstr{entrysubtype}{individual}{}{\skipentry}%
+ \iffieldequalstr{status}{submitted}{}{\skipentry}%
+}
+\defbibcheck{Funding:IndividualUnsuccessful}{%
+ \ifentrytype{funding}{}{\skipentry}%
+ \iffieldequalstr{entrysubtype}{individual}{}{\skipentry}%
+ \iffieldequalstr{status}{unsuccessful}{}{\skipentry}%
+}
+\defbibcheck{Funding:IndividualCurrentAndPending}{%
+ \ifentrytype{funding}{}{\skipentry}%
+ \iffieldequalstr{entrysubtype}{individual}{}{\skipentry}%
+ \iffieldequalstr{status}{ongoing}{}{%
+ \iffieldequalstr{status}{pending}{}{%
+ \iffieldequalstr{status}{submitted}{}{\skipentry}%
+ }%
+ }%
+}
+\defbibcheck{Funding:IndividualNotUnsuccessful}{%
+ \ifentrytype{funding}{}{\skipentry}%
+ \iffieldequalstr{entrysubtype}{individual}{}{\skipentry}%
+ \iffieldequalstr{status}{unsuccessful}{\skipentry}{}%
+}
+
+\defbibcheck{Funding:Institutional}{%
+ \ifentrytype{funding}{}{\skipentry}%
+ \iffieldequalstr{entrysubtype}{institutional}{}{\skipentry}%
+}
+\defbibcheck{Funding:InstitutionalCompleted}{%
+ \ifentrytype{funding}{}{\skipentry}%
+ \iffieldequalstr{entrysubtype}{institutional}{}{\skipentry}%
+ \iffieldequalstr{status}{completed}{}{\skipentry}%
+}
+\defbibcheck{Funding:InstitutionalOngoing}{%
+ \ifentrytype{funding}{}{\skipentry}%
+ \iffieldequalstr{entrysubtype}{institutional}{}{\skipentry}%
+ \iffieldequalstr{status}{ongoing}{}{\skipentry}%
+}
+\defbibcheck{Funding:InstitutionalPending}{%
+ \ifentrytype{funding}{}{\skipentry}%
+ \iffieldequalstr{entrysubtype}{institutional}{}{\skipentry}%
+ \iffieldequalstr{status}{pending}{}{\skipentry}%
+}
+\defbibcheck{Funding:InstitutionalSubmitted}{%
+ \ifentrytype{funding}{}{\skipentry}%
+ \iffieldequalstr{entrysubtype}{institutional}{}{\skipentry}%
+ \iffieldequalstr{status}{submitted}{}{\skipentry}%
+}
+\defbibcheck{Funding:InstitutionalUnsuccessful}{%
+ \ifentrytype{funding}{}{\skipentry}%
+ \iffieldequalstr{entrysubtype}{institutional}{}{\skipentry}%
+ \iffieldequalstr{status}{unsuccessful}{}{\skipentry}%
+}
+\defbibcheck{Funding:InstitutionalCurrentAndPending}{%
+ \ifentrytype{funding}{}{\skipentry}%
+ \iffieldequalstr{entrysubtype}{institutional}{}{\skipentry}%
+ \iffieldequalstr{status}{ongoing}{}{%
+ \iffieldequalstr{status}{pending}{}{%
+ \iffieldequalstr{status}{submitted}{}{\skipentry}%
+ }%
+ }%
+}
+\defbibcheck{Funding:InstitutionalNotUnsuccessful}{%
+ \ifentrytype{funding}{}{\skipentry}%
+ \iffieldequalstr{entrysubtype}{institutional}{}{\skipentry}%
+ \iffieldequalstr{status}{unsuccessful}{\skipentry}{}%
+}
+
+% PRESENTATION
+\defbibcheck{Presentation}{%
+ \ifentrytype{presentation}{}{\skipentry}%
+}
+\defbibcheck{Presentation:Contributed}{%
+ \ifentrytype{presentation}{}{\skipentry}%
+ \iffieldequalstr{presentationtype}{contributed}{}{\skipentry}%
+}
+\defbibcheck{Presentation:Internal}{%
+ \ifentrytype{presentation}{}{\skipentry}%
+ \iffieldequalstr{presentationtype}{internal}{}{\skipentry}%
+}
+\defbibcheck{Presentation:Invited}{%
+ \ifentrytype{presentation}{}{\skipentry}%
+ \iffieldequalstr{presentationtype}{invited}{}{\skipentry}%
+}
+\defbibcheck{Presentation:Job}{%
+ \ifentrytype{presentation}{}{\skipentry}%
+ \iffieldequalstr{presentationtype}{job}{}{\skipentry}%
+}
+\defbibcheck{Presentation:Keynote}{%
+ \ifentrytype{presentation}{}{\skipentry}%
+ \iffieldequalstr{presentationtype}{keynote}{}{\skipentry}
+}
+\defbibcheck{Presentation:NotInternalOrJob}{%
+ \ifentrytype{presentation}{}{\skipentry}%
+ \iffieldequalstr{presentationtype}{internal}{\skipentry}{%
+ \iffieldequalstr{presentationtype}{job}{\skipentry}{}%
+ }%
+}
+
+\defbibcheck{Presentation:Poster}{%
+ \ifentrytype{presentation}{}{\skipentry}%
+ \iffieldequalstr{entrysubtype}{poster}{}{\skipentry}%
+}
+\defbibcheck{Presentation:PosterContributed}{%
+ \ifentrytype{presentation}{}{\skipentry}%
+ \iffieldequalstr{entrysubtype}{poster}{}{\skipentry}%
+ \iffieldequalstr{presentationtype}{contributed}{}{\skipentry}%
+}
+\defbibcheck{Presentation:PosterInternal}{%
+ \ifentrytype{presentation}{}{\skipentry}%
+ \iffieldequalstr{entrysubtype}{poster}{}{\skipentry}%
+ \iffieldequalstr{presentationtype}{internal}{}{\skipentry}%
+}
+\defbibcheck{Presentation:PosterInvited}{%
+ \ifentrytype{presentation}{}{\skipentry}%
+ \iffieldequalstr{entrysubtype}{poster}{}{\skipentry}%
+ \iffieldequalstr{presentationtype}{invited}{}{\skipentry}%
+}
+\defbibcheck{Presentation:PosterJob}{%
+ \ifentrytype{presentation}{}{\skipentry}%
+ \iffieldequalstr{entrysubtype}{poster}{}{\skipentry}%
+ \iffieldequalstr{presentationtype}{job}{}{\skipentry}%
+}
+\defbibcheck{Presentation:PosterKeynote}{%
+ \ifentrytype{presentation}{}{\skipentry}%
+ \iffieldequalstr{entrysubtype}{poster}{}{\skipentry}%
+ \iffieldequalstr{presentationtype}{keynote}{}{\skipentry}
+}
+\defbibcheck{Presentation:PosterNotInternalOrJob}{%
+ \ifentrytype{presentation}{}{\skipentry}%
+ \iffieldequalstr{entrysubtype}{poster}{}{\skipentry}%
+ \iffieldequalstr{presentationtype}{internal}{\skipentry}{%
+ \iffieldequalstr{presentationtype}{job}{\skipentry}{}%
+ }%
+}
+
+\defbibcheck{Presentation:Talk}{%
+ \ifentrytype{presentation}{}{\skipentry}%
+ \iffieldequalstr{entrysubtype}{talk}{}{\skipentry}%
+}
+\defbibcheck{Presentation:TalkContributed}{%
+ \ifentrytype{presentation}{}{\skipentry}%
+ \iffieldequalstr{entrysubtype}{talk}{}{\skipentry}%
+ \iffieldequalstr{presentationtype}{contributed}{}{\skipentry}%
+}
+\defbibcheck{Presentation:TalkInternal}{%
+ \ifentrytype{presentation}{}{\skipentry}%
+ \iffieldequalstr{entrysubtype}{talk}{}{\skipentry}%
+ \iffieldequalstr{presentationtype}{internal}{}{\skipentry}%
+}
+\defbibcheck{Presentation:TalkInvited}{%
+ \ifentrytype{presentation}{}{\skipentry}%
+ \iffieldequalstr{entrysubtype}{talk}{}{\skipentry}%
+ \iffieldequalstr{presentationtype}{invited}{}{\skipentry}%
+}
+\defbibcheck{Presentation:TalkJob}{%
+ \ifentrytype{presentation}{}{\skipentry}%
+ \iffieldequalstr{entrysubtype}{talk}{}{\skipentry}%
+ \iffieldequalstr{presentationtype}{job}{}{\skipentry}%
+}
+\defbibcheck{Presentation:TalkKeynote}{%
+ \ifentrytype{presentation}{}{\skipentry}%
+ \iffieldequalstr{entrysubtype}{talk}{}{\skipentry}%
+ \iffieldequalstr{presentationtype}{keynote}{}{\skipentry}
+}
+\defbibcheck{Presentation:TalkNotInternalOrJob}{%
+ \ifentrytype{presentation}{}{\skipentry}%
+ \iffieldequalstr{entrysubtype}{talk}{}{\skipentry}%
+ \iffieldequalstr{presentationtype}{internal}{\skipentry}{%
+ \iffieldequalstr{presentationtype}{job}{\skipentry}{}%
+ }%
+}
+
+% STUDENT
+\defbibcheck{Student}{%
+ \ifentrytype{student}{}{\skipentry}%
+}
+\defbibcheck{Student:Masters}{%
+ \ifentrytype{student}{}{\skipentry}%
+ \iffieldequalstr{entrysubtype}{masters}{}{\skipentry}%
+}
+\defbibcheck{Student:Phd}{%
+ \ifentrytype{student}{}{\skipentry}%
+ \iffieldequalstr{entrysubtype}{phd}{}{\skipentry}%
+}
+\defbibcheck{Student:Postdoc}{%
+ \ifentrytype{student}{}{\skipentry}%
+ \iffieldequalstr{entrysubtype}{postdoc}{}{\skipentry}%
+}
+\defbibcheck{Student:Undergraduate}{%
+ \ifentrytype{student}{}{\skipentry}%
+ \iffieldequalstr{entrysubtype}{undergraduate}{}{\skipentry}%
+}
+\defbibcheck{Student:NotUndergraduate}{%
+ \ifentrytype{student}{}{\skipentry}%
+ \iffieldequalstr{entrysubtype}{undergraduate}{\skipentry}{}%
+}
+\defbibcheck{Student:Externalexaminer}{%
+ \ifentrytype{student}{}{\skipentry}%
+ \iffieldequalstr{role}{externalexaminer}{}{\skipentry}%
+}
+\defbibcheck{Student:Internalexaminer}{%
+ \ifentrytype{student}{}{\skipentry}%
+ \iffieldequalstr{role}{internalexaminer}{}{\skipentry}%
+}
+\defbibcheck{Student:Secondsupervisor}{%
+ \ifentrytype{student}{}{\skipentry}%
+ \iffieldequalstr{role}{secondsupervisor}{}{\skipentry}%
+}
+\defbibcheck{Student:Supervisor}{%
+ \ifentrytype{student}{}{\skipentry}%
+ \iffieldequalstr{role}{supervisor}{}{\skipentry}%
+}
+\defbibcheck{Student:NotExaminer}{%
+ \ifentrytype{student}{}{\skipentry}%
+ \iffieldequalstr{role}{internalexaminer}{\skipentry}{}%
+ \iffieldequalstr{role}{externalexaminer}{\skipentry}{}%
+}
+\defbibcheck{Student:MastersExternalexaminer}{%
+ \ifentrytype{student}{}{\skipentry}%
+ \iffieldequalstr{entrysubtype}{masters}{}{\skipentry}%
+ \iffieldequalstr{role}{externalexaminer}{}{\skipentry}%
+}
+\defbibcheck{Student:MastersInternalexaminer}{%
+ \ifentrytype{student}{}{\skipentry}%
+ \iffieldequalstr{entrysubtype}{masters}{}{\skipentry}%
+ \iffieldequalstr{role}{internalexaminer}{}{\skipentry}%
+}
+\defbibcheck{Student:MastersSecondsupervisor}{%
+ \ifentrytype{student}{}{\skipentry}%
+ \iffieldequalstr{entrysubtype}{masters}{}{\skipentry}%
+ \iffieldequalstr{role}{secondsupervisor}{}{\skipentry}%
+}
+\defbibcheck{Student:MastersSupervisor}{%
+ \ifentrytype{student}{}{\skipentry}%
+ \iffieldequalstr{entrysubtype}{masters}{}{\skipentry}%
+ \iffieldequalstr{role}{supervisor}{}{\skipentry}%
+}
+\defbibcheck{Student:MastersNotExaminer}{%
+ \ifentrytype{student}{}{\skipentry}%
+ \iffieldequalstr{entrysubtype}{masters}{}{\skipentry}%
+ \iffieldequalstr{role}{internalexaminer}{\skipentry}{}%
+ \iffieldequalstr{role}{externalexaminer}{\skipentry}{}%
+}
+\defbibcheck{Student:PhdExternalexaminer}{%
+ \ifentrytype{student}{}{\skipentry}%
+ \iffieldequalstr{entrysubtype}{phd}{}{\skipentry}%
+ \iffieldequalstr{role}{externalexaminer}{}{\skipentry}%
+}
+\defbibcheck{Student:PhdInternalexaminer}{%
+ \ifentrytype{student}{}{\skipentry}%
+ \iffieldequalstr{entrysubtype}{phd}{}{\skipentry}%
+ \iffieldequalstr{role}{internalexaminer}{}{\skipentry}%
+}
+\defbibcheck{Student:PhdSecondsupervisor}{%
+ \ifentrytype{student}{}{\skipentry}%
+ \iffieldequalstr{entrysubtype}{phd}{}{\skipentry}%
+ \iffieldequalstr{role}{secondsupervisor}{}{\skipentry}%
+}
+\defbibcheck{Student:PhdSupervisor}{%
+ \ifentrytype{student}{}{\skipentry}%
+ \iffieldequalstr{entrysubtype}{phd}{}{\skipentry}%
+ \iffieldequalstr{role}{supervisor}{}{\skipentry}%
+}
+\defbibcheck{Student:PhdNotExaminer}{%
+ \ifentrytype{student}{}{\skipentry}%
+ \iffieldequalstr{entrysubtype}{phd}{}{\skipentry}%
+ \iffieldequalstr{role}{internalexaminer}{\skipentry}{}%
+ \iffieldequalstr{role}{externalexaminer}{\skipentry}{}%
+}
+\defbibcheck{Student:PostdocExternalexaminer}{%
+ \ifentrytype{student}{}{\skipentry}%
+ \iffieldequalstr{entrysubtype}{postdoc}{}{\skipentry}%
+ \iffieldequalstr{role}{externalexaminer}{}{\skipentry}%
+}
+\defbibcheck{Student:PostdocInternalexaminer}{%
+ \ifentrytype{student}{}{\skipentry}%
+ \iffieldequalstr{entrysubtype}{postdoc}{}{\skipentry}%
+ \iffieldequalstr{role}{internalexaminer}{}{\skipentry}%
+}
+\defbibcheck{Student:PostdocSecondsupervisor}{%
+ \ifentrytype{student}{}{\skipentry}%
+ \iffieldequalstr{entrysubtype}{postdoc}{}{\skipentry}%
+ \iffieldequalstr{role}{secondsupervisor}{}{\skipentry}%
+}
+\defbibcheck{Student:PostdocSupervisor}{%
+ \ifentrytype{student}{}{\skipentry}%
+ \iffieldequalstr{entrysubtype}{postdoc}{}{\skipentry}%
+ \iffieldequalstr{role}{supervisor}{}{\skipentry}%
+}
+\defbibcheck{Student:PostdocNotExaminer}{%
+ \ifentrytype{student}{}{\skipentry}%
+ \iffieldequalstr{entrysubtype}{postdoc}{}{\skipentry}%
+ \iffieldequalstr{role}{internalexaminer}{\skipentry}{}%
+ \iffieldequalstr{role}{externalexaminer}{\skipentry}{}%
+}
+\defbibcheck{Student:UndergraduateExternalexaminer}{%
+ \ifentrytype{student}{}{\skipentry}%
+ \iffieldequalstr{entrysubtype}{undergraduate}{}{\skipentry}%
+ \iffieldequalstr{role}{externalexaminer}{}{\skipentry}%
+}
+\defbibcheck{Student:UndergraduateInternalexaminer}{%
+ \ifentrytype{student}{}{\skipentry}%
+ \iffieldequalstr{entrysubtype}{undergraduate}{}{\skipentry}%
+ \iffieldequalstr{role}{internalexaminer}{}{\skipentry}%
+}
+\defbibcheck{Student:UndergraduateSecondsupervisor}{%
+ \ifentrytype{student}{}{\skipentry}%
+ \iffieldequalstr{entrysubtype}{undergraduate}{}{\skipentry}%
+ \iffieldequalstr{role}{secondsupervisor}{}{\skipentry}%
+}
+\defbibcheck{Student:UndergraduateSupervisor}{%
+ \ifentrytype{student}{}{\skipentry}%
+ \iffieldequalstr{entrysubtype}{undergraduate}{}{\skipentry}%
+ \iffieldequalstr{role}{supervisor}{}{\skipentry}%
+}
+\defbibcheck{Student:UndergraduateNotExaminer}{%
+ \ifentrytype{student}{}{\skipentry}%
+ \iffieldequalstr{entrysubtype}{undergraduate}{}{\skipentry}%
+ \iffieldequalstr{role}{internalexaminer}{\skipentry}{}%
+ \iffieldequalstr{role}{externalexaminer}{\skipentry}{}%
+}
+\defbibcheck{Student:NotUndergraduateExternalexaminer}{%
+ \ifentrytype{student}{}{\skipentry}%
+ \iffieldequalstr{entrysubtype}{undergraduate}{\skipentry}{}%
+ \iffieldequalstr{role}{externalexaminer}{}{\skipentry}%
+}
+\defbibcheck{Student:NotUndergraduateInternalexaminer}{%
+ \ifentrytype{student}{}{\skipentry}%
+ \iffieldequalstr{entrysubtype}{undergraduate}{\skipentry}{}%
+ \iffieldequalstr{role}{internalexaminer}{}{\skipentry}%
+}
+\defbibcheck{Student:NotUndergraduateSecondsupervisor}{%
+ \ifentrytype{student}{}{\skipentry}%
+ \iffieldequalstr{entrysubtype}{undergraduate}{\skipentry}{}%
+ \iffieldequalstr{role}{secondsupervisor}{}{\skipentry}%
+}
+\defbibcheck{Student:NotUndergraduateSupervisor}{%
+ \ifentrytype{student}{}{\skipentry}%
+ \iffieldequalstr{entrysubtype}{undergraduate}{\skipentry}{}%
+ \iffieldequalstr{role}{supervisor}{}{\skipentry}%
+}
+\defbibcheck{Student:NotUndergraduateNotExaminer}{%
+ \ifentrytype{student}{}{\skipentry}%
+ \iffieldequalstr{entrysubtype}{undergraduate}{\skipentry}{}%
+ \iffieldequalstr{role}{internalexaminer}{\skipentry}{}%
+ \iffieldequalstr{role}{externalexaminer}{\skipentry}{}%
+}
+
+% TEACHING
+\defbibcheck{Teaching}{%
+ \ifentrytype{teaching}{}{\skipentry}%
+}
+\defbibcheck{Teaching:Graduate}{%
+ \ifentrytype{teaching}{}{\skipentry}%
+ \iffieldequalstr{entrysubtype}{graduate}{}{\skipentry}%
+}
+\defbibcheck{Teaching:Undergraduate}{%
+ \ifentrytype{teaching}{}{\skipentry}%
+ \iffieldequalstr{entrysubtype}{undergraduate}{}{\skipentry}%
+}
+
+% Create a biblatex/biber MD5 hash from the parts of a name and store the hashes so that the name can be emphasized and/or deleted throughout the CV. If seems like \zap@space and ExplSyntax do not play nice together. There is probably a much cleaner way of doing this. Basically, the \highlightname macro concatenates the name parts, strips any spaces, calculates the hash, and then converts the hash to all lower case letters. The code does not handle periods in the name the same way that biblatex/biber does and therefore the input to \highlightname should probably not have periods.
+\newcommand{\highlightname}[4]{% family, given, prefix, suffix
+ \edef\cv@blx@highlightname@hash{\zap@space #1#2#3#4 \@empty}%
+ \ifxetex%
+ \expandafter\edef\expandafter\cv@blx@highlightname@hash\expandafter{%
+ \expandafter\mdfivesum\expandafter{%
+ \cv@blx@highlightname@hash%
+ }%
+ }%
+ \else%
+ \expandafter\edef\expandafter\cv@blx@highlightname@hash\expandafter{%
+ \expandafter\pdfmdfivesum\expandafter{%
+ \cv@blx@highlightname@hash%
+ }%
+ }%
+ \fi%
+ \cv@blx@highlightname%
+}
+
+\ExplSyntaxOn
+ \newcommand{\cv@blx@highlightname}{%
+ \edef\cv@blx@highlightname@hash{%
+ \text_lowercase:n{\cv@blx@highlightname@hash}%
+ }%
+ \listxadd{\cv@blx@highlightname@hash@list}{%
+ \cv@blx@highlightname@hash%
+ }%
+ }
+\ExplSyntaxOff
+
+% Emphasized a name throughout the CV. We patch the name formats and not the underlying bib macros, since this allows us to compare the name hash instead of the name parts. This seems more robust to things like initials and different name ordering schemes
+\newcommand{\cv@blx@highlightname@format}{\textbf}
+\newcommand{\cv@blx@highlightname@patch}{%
+ \begingroup% Needs to be ended by an \xapptonameformat
+ \xifinlist{\thefield{hash}}{\cv@blx@highlightname@hash@list}{%
+ \let\mkbibnamegiven=\cv@blx@highlightname@format%
+ \let\mkbibnamefamily=\cv@blx@highlightname@format%
+ \let\mkbibnameprefix=\cv@blx@highlightname@format%
+ \let\mkbibnamesuffix=\cv@blx@highlightname@format%
+ }{}%
+}
+\xpretonameformat{given-family}{\cv@blx@highlightname@patch}{}{}
+\xapptonameformat{given-family}{\endgroup}{}{}
+\xpretonameformat{family-given}{\cv@blx@highlightname@patch}{}{}
+\xapptonameformat{family-given}{\endgroup}{}{}
+\xpretonameformat{family-given/given-family}{\cv@blx@highlightname@patch}{}{}
+\xapptonameformat{family-given/given-family}{\endgroup}{}{}
+\xpretonameformat{initsonly}{\cv@blx@highlightname@patch}{}{}
+\xapptonameformat{initsonly}{\endgroup}{}{}
+
+% Delete a name from a list of names. Unlike the emphasizing the name, the deletion is done on a per type (i.e., author, editor, presenter) basis.
+\newcommand{\cv@blx@deletename}[1]{%
+ \expandafter\forlistloop\expandafter{\expandafter\cv@blx@deletename@do\expandafter{\NewValue}{#1}}{\cv@blx@highlightname@hash@list}%
+}
+\ExplSyntaxOn
+ \NewDocumentCommand{\cv@blx@deletename@do}{mmm}{% Name information, Type, Hash
+ \tl_set:Nn\l_tmpa_tl{#1}%
+ \str_if_in:nnTF{#1}{hash=#3}{%
+ \numdef\NewCount{\NewCount-1}%
+ \regex_replace_all:nnN{\cB\{\cB\{.*?hash=#3.*?\cE\}\cE\}\cE\}}{}\l_tmpa_tl%
+ \regex_replace_all:nnN{\cB\{\cB\{.*?hash=#3.*?\cE\}.*?givenun=0.*?\cE\}\cE\}}{}\l_tmpa_tl%
+ \edef\NewValue{\l_tmpa_tl}%
+ \blx@bbl@addfield{#2deleted}{1}%
+ }{}%
+ }
+\ExplSyntaxOff
+
+\newbibmacro{nameDeleted}{%
+ \iffieldequalstr{\currentname deleted}{1}{%
+ \bibstring{with}\addspace%
+ }{%
+ \bibstring{by}\addspace%
+ }%
+}
+\renewcommand{\cv@blx@tempa}[1]{%
+ \DeclareNameInputHandler{#1}{%
+ \iftoggle{cv@blx:delete:#1}{\cv@blx@deletename{#1}}{}%
+ }%
+ \DeclareNameWrapperFormat{#1}{%
+ \iftoggle{cv@blx:delete:#1}{\usebibmacro{nameDeleted}}{}%
+ ##1%
+ }%
+}
+\forcsvlist{\cv@blx@tempa}{afterword, annotator, author, bookauthor, %
+ commentator, editor, editora, editorb, editorc, foreword, holder, %
+ introduction, namea, nameb, namec, translator, shortauthor, %
+ shorteditor, advisor, committee, name, presenter%
+}
+
+% Some sections of a CV should have numbered items and some should not. We need to define unnumbered and numbered (with descending labels) bibliography environments. In order for this to work, we need to create a new totcounter every time a new bibliography is started. I think this breaks if there is more than one refsection in the CV, but I am not sure why there would be more than one refsection in a CV.
+\newcounter{cv@blx@bib}
+\newtotcounter{cv@blx@ref@0}
+\xpatchcmd{\printbibliography}{\begingroup}{%
+ \stepcounter{cv@blx@bib}%
+ \edef\@tempa{cv@blx@ref@\arabic{cv@blx@bib}}%
+ \expandafter\newtotcounter\expandafter{\@tempa}%
+ \begingroup
+}{}{}
+
+\newrobustcmd{\mkbibdown}[1]{%
+ \stepcounter{cv@blx@ref@\arabic{cv@blx@bib}}%
+ \begingroup%
+ \edef\@tempa{cv@blx@ref@\arabic{cv@blx@bib}}%
+ \number\numexpr%
+ \expandafter\totvalue\expandafter{\@tempa}+1-#1%
+ \relax%
+ \endgroup%
+}
+
+\DeclareFieldFormat{labelnumber}{\mkbibdown{#1}}
+\DeclareFieldFormat{labelnumberwidth}{\mkbibbrackets{#1}}
+
+\newcommand{\cv@blx@list}{%
+ \setlength{\labelwidth}{\labelnumberwidth}%
+ \setlength{\leftmargin}{\labelwidth}%
+ \setlength{\labelsep}{\biblabelsep}%
+ \addtolength{\leftmargin}{\labelsep}%
+ \setlength{\itemsep}{\bibitemsep}%
+ \setlength{\parsep}{\bibparsep}%
+ \addtolength{\leftmargin}{\bibhang}%
+ \setlength{\itemindent}{-\bibhang}%
+}
+
+\newcommand{\cv@blx@sublist}{%
+ \setlength{\labelwidth}{\labelnumberwidth}%
+ \setlength{\leftmargin}{\labelwidth}%
+ \setlength{\labelsep}{\biblabelsep}%
+ \addtolength{\leftmargin}{\labelsep}%
+ \setlength{\itemsep}{\bibitemsep}%
+ \setlength{\parsep}{\bibparsep}%
+ \addtolength{\leftmargin}{2\bibhang}%
+ \setlength{\itemindent}{-\bibhang}%
+}
+
+\defbibenvironment{numbered}{%
+ \list{%
+ \printtext[labelnumberwidth]{%
+ \printfield{prefixnumber}%
+ \printfield{labelnumber}%
+ }%
+ }{\cv@blx@list}%
+ \renewcommand*{\makelabel}[1]{\hss##1}%
+}{\endlist}{\item}
+
+\defbibenvironment{unnumbered}{%
+ \list{}{\cv@blx@list}%
+ \renewcommand*{\makelabel}[1]{\hss##1}%
+}{\endlist}{\item}
+
+% Data inheritances need to be defined for the related types
+\DeclareDataInheritance{committee}{committee}{
+ \inherit{entrysubtype}{entrysubtype}
+ \noinherit{semesters}
+}
+
+\DeclareDataInheritance{school}{degree}{
+ \noinherit{degreelist}
+ \noinherit{gpa}
+}
+
+\DeclareDataInheritance{teaching}{teaching}{
+ \inherit{entrysubtype}{entrysubtype}
+}
+
+% Deal with the verbose style options
+\iftoggle{cv@blx:verbose:committee}{
+ \DeclareStyleSourcemap{
+ \maps[datatype = bibtex]{
+ \map[overwrite]{
+ \pertype{committee}
+ \step[fieldsource = semesters, fieldset = options,
+ fieldvalue = {skipbib = true}]
+ }
+ }
+ }
+}{
+ \DeclareStyleSourcemap{
+ \maps[datatype = bibtex]{
+ \map[overwrite]{
+ \pertype{committee}
+ \step[fieldsource = crossref, fieldset = options,
+ fieldvalue = {skipbib = true}]
+ }
+ }
+ }
+}
+
+\DeclareStyleSourcemap{
+ \maps[datatype = bibtex]{
+ \map[overwrite]{
+ \pertype{teaching}
+ \step[fieldsource = crossref, fieldset = options,
+ fieldvalue = {skipbib = true}]
+ }
+ }
+ \maps[datatype = bibtex]{
+ \map[overwrite]{
+ \pertype{degree}
+ \step[fieldsource = crossref, fieldset = options,
+ fieldvalue = {skipbib = true}]
+ }
+ }
+}
+
+\iftoggle{cv@blx:verbose:education}{
+ \toggletrue{cv@blx:education:advisor}
+ \toggletrue{cv@blx:education:committee}
+ \toggletrue{cv@blx:education:gpa}
+ \toggletrue{cv@blx:education:honors}
+ \toggletrue{cv@blx:education:major}
+ \toggletrue{cv@blx:education:minor}
+ \toggletrue{cv@blx:education:title}
+}{}
diff --git a/macros/latex/contrib/biblatex-contrib/biblatex-cv/doc/biblatex-cv.bib b/macros/latex/contrib/biblatex-contrib/biblatex-cv/doc/biblatex-cv.bib
new file mode 100644
index 0000000000..98583e3777
--- /dev/null
+++ b/macros/latex/contrib/biblatex-contrib/biblatex-cv/doc/biblatex-cv.bib
@@ -0,0 +1,347 @@
+%@Abstract{Abstract:,
+ %Author = {},
+ %Title = {},
+ %Journaltitle = {},
+ %Date = {},
+ %Entrysubtype = {},
+ %PresentationType = {},
+ %Presenter = {},
+%}
+
+%@Committee{Committee:,
+ %Date = {},
+ %Entrysubtype = {},
+ %Title = {},
+ %Institution = {},
+ %Location = {},
+ %Department = {},
+ %Role = {},
+ %Semesters = {},
+ %crossref = {},
+%}
+
+%@School{School:,
+ %Date = {},
+ %Institution = {},
+ %Location = {},
+ %Department = {},
+ %GPA = {},
+ %Honors = {},
+ %DegreeList = {},
+%}
+
+%@Degree{Degree:,
+ %Date = {},
+ %Institution = {},
+ %Location = {},
+ %Department = {},
+ %Degree = {},
+ %GPA = {},
+ %Honors = {},
+ %Concentration = {},
+ %Advisor = {},
+ %Committee = {},
+ %Title = {},
+ %crossref = {},
+%}
+
+%@Funding{Funding:,
+ %Date = {},
+ %Entrysubtype = {},
+ %Status = {},
+ %Author = {},
+ %Funder = {},
+ %Title = {},
+ %Role = {},
+ %Amount = {},
+ %Currency = {},
+ %Number = {},
+ %Type = {},
+%}
+
+%@Presentation{Presentation:,
+ %Date = {},
+ %Entrysubtype = {},
+ %Author = {},
+ %PresentationType = {},
+ %Presenter = {},
+ %Title = {},
+ %Institution = {},
+ %Location = {},
+ %Department = {},
+%}
+
+%@Student{Student:,
+ %Date = {},
+ %Entrysubtype = {},
+ %Name = {},
+ %Role = {},
+ %Institution = {},
+ %Location = {},
+ %Department = {},
+%}
+
+%@Teaching{Teaching:,
+ %Date = {},
+ %Entrysubtype = {},
+ %Title = {},
+ %Department = {},
+ %Institution = {},
+ %Location = {},
+ %Number = {},
+ %NumLectures = {},
+ %NumStudents = {},
+ %Role = {},
+ %classes = {},
+%}
+
+@XData{Author:Me,
+ Author = {Doe, Jon},
+}
+
+@XData{Author:Us,
+ Author = {Doe, Jon and Smith, Alice and Jones, Bob},
+}
+
+@XData{Author:Them,
+ Author = {Smith, Alice and Jones, Bob},
+}
+
+@XData{Institution:Departmental,
+ Institution = {My Institution},
+ Location = {My City},
+ Department = {My Department},
+}
+
+@XData{Institution:External,
+ Institution = {Their Institution},
+ Location = {Their City},
+ Department = {Their Department},
+}
+
+@XData{Institution:Institutional,
+ Institution = {My Institution},
+ Location = {My City},
+}
+
+@XData{Presenters:Me,
+ Presenter = {Doe, Jon},
+}
+
+@XData{Presenters:Us,
+ Presenter = {Doe, Jon and Smith, Alice and Jones, Bob},
+}
+
+@XData{Presenters:Them,
+ Presenter = {Smith, Alice and Jones, Bob},
+}
+
+@Abstract{Abstract:PosterContributedJoint,
+ xdata = {Author:Us, Presenters:Me},
+ Title = {A Contributed Poster Abstract},
+ Journaltitle = {Journal of Poster Abstracts},
+ Date = {2020-01-01},
+ Entrysubtype = {poster},
+ PresentationType = {contributed},
+}
+
+@Abstract{Abstract:TalkInternalSolo,
+ xdata = {Author:Them, Presenters:Them},
+ Title = {An Internal Talk Abstract},
+ Journaltitle = {Journal of Talk Abstracts},
+ Date = {2019-01-01},
+ Entrysubtype = {talk},
+ PresentationType = {internal},
+}
+
+@Abstract{Abstract:TalkInvitedOther,
+ xdata = {Author:Me, Presenters:Us},
+ Title = {An Invited Talk Abstract},
+ Journaltitle = {Journal of Talk Abstracts},
+ Date = {2018-01-01},
+ Entrysubtype = {talk},
+ PresentationType = {invited},
+}
+
+@Committee{Committee:Departmental,
+ Semesters = {Committee:DepartmentalSemesterA, Committee:DepartmentalSemesterB},
+ xdata = {Institution:Departmental},
+ Date = {2010-23/2019-21},
+ Entrysubtype = {departmental},
+ Title = {An Example Departmental Committee With Semesters},
+
+ Role = {},
+}
+
+@Committee{Committee:DepartmentalSemesterA,
+ crossref = {Committee:Departmental},
+ Date = {2010-23/2015-21},
+ Role = {Secretary},
+}
+
+@Committee{Committee:DepartmentalSemesterB,
+ crossref = {Committee:Departmental},
+ Date = {2015-22/2019-21},
+ Role = {Chair},
+}
+
+@Committee{Committee:External,
+ xdata = {Institution:External},
+ Date = {2015-23},
+ Entrysubtype = {external},
+ Title = {An Example External Committee Without Semesters},
+ Role = {Consultant},
+}
+
+@Committee{Committee:Institutional,
+ xdata = {Institution:Institutional},
+ Date = {2017-21/2019-22},
+ Entrysubtype = {institutional},
+ Title = {An Example Institutional},
+ Role = {},
+}
+
+@School{School:Degree,
+ Date = {2010-23/2019-21},
+ Institution = {My Institution},
+ Location = {My City},
+ GPA = {3.5},
+ Honors = {Institution Honors},
+ DegreeList = {Degree:MS, Degree:PhD},
+}
+
+@Degree{Degree:PhD,
+ Date = {2019-21},
+ Department = {My PhD Department},
+ Degree = {PhD},
+ GPA = {4.0},
+ Honors = {PhD Honors},
+ Concentration = {PhD Concentration},
+ Advisor = {Jones, Alice},
+ Committee = {Jones, Alice and Smith, Bob},
+ Title = {My PhD Title},
+ crossref = {School:Degree},
+}
+
+@Degree{Degree:MS,
+ Date = {2012-21},
+ Department = {My MS Department},
+ Degree = {MS},
+ GPA = {3.0},
+ Advisor = {Smith, Bob},
+ Committee = {Jones, Alice and Smith, Bob},
+ Title = {My MS Title},
+ crossref = {School:Degree},
+}
+
+@School{School:NoDegree,
+ xdata = {Institution:External},
+ Date = {2009-21},
+ GPA = {2.0},
+ Honors = {My Other Honors},
+}
+
+@Funding{Funding:IndividualOngoing,
+ xdata = {Author:Us},
+ Date = {2019-01/2021-01},
+ Entrysubtype = {individual},
+ Status = {ongoing},
+ Funder = {Funding Agency},
+ Title = {A Grant Title},
+ Role = {pi},
+ Amount = {100,000,000},
+ Currency = {\$},
+ Number = {123-456-abc},
+ Type = {Research Grant},
+}
+
+@Funding{Funding:InstitutionalCompleted,
+ xdata = {Author:Us},
+ Date = {2010-01/2019-01},
+ Entrysubtype = {Institutional},
+ Status = {completed},
+ Funder = {A Different Funding Agency},
+ Title = {A Different Grant Title},
+ Role = {ci},
+ Amount = {1,000,000},
+ Currency = {\pounds},
+ Number = {abc-def-123},
+ Type = {Fellowship},
+}
+
+
+@Presentation{Presentation:PosterContributedJoint,
+ xdata = {Author:Us, Presenters:Me, Institution:External},
+ Title = {A Contributed Poster Presentation},
+ Date = {2020-01-01},
+ Entrysubtype = {poster},
+ PresentationType = {contributed},
+}
+
+@Presentation{Presentation:TalkInternalSolo,
+ xdata = {Author:Them, Presenters:Them, Institution:Departmental},
+ Title = {A Departmental Talk Presentation},
+ Date = {2019-01-01},
+ Entrysubtype = {talk},
+ PresentationType = {internal},
+}
+
+@Presentation{Presentation:TalkInvitedOther,
+ xdata = {Author:Me, Presenters:Us, Institution:Institutional},
+ Title = {An Institutional Invited Talk Presentation},
+ Date = {2018-01-01},
+ Entrysubtype = {talk},
+ PresentationType = {invited},
+}
+
+@Student{Student:SupervisorMS,
+ xdata = {Institution:Departmental},
+ Date = {2018-23/2020-21},
+ Entrysubtype = {masters},
+ Name = {Smith, Alice},
+ Role = {supervisor},
+}
+
+@Student{Student:InternalExaminerPhD,
+ xdata = {Institution:External},
+ Date = {2016-24/2018-22},
+ Entrysubtype = {phd},
+ Name = {Jones, Bob},
+ Role = {internalexaminer},
+}
+
+@Teaching{Teaching:Graduate,
+ xdata = {Institution:Departmental},
+ Date = {2020-23},
+ Entrysubtype = {graduate},
+ Title = {A Graduate Class},
+ Number = {EE401},
+ NumLectures = {36},
+ NumStudents = {100},
+ Role = {Instructor of Record},
+}
+
+@Teaching{Teaching:Undergraduate,
+ xdata = {Institution:Departmental},
+ Date = {2019-21/2020-22},
+ Entrysubtype = {undergraduate},
+ Title = {An Undergraduate Class},
+ Number = {EE101},
+ classes = {Teaching:Undergraduate20, Teaching:Undergraduate19},
+}
+
+@Teaching{Teaching:Undergraduate20,
+ crossref = {Teaching:Undergraduate},
+ Date = {2020-22},
+ NumLectures = {36},
+ NumStudents = {100},
+ Role = {Instructor of Record},
+}
+
+@Teaching{Teaching:Undergraduate19,
+ crossref = {Teaching:Undergraduate},
+ Date = {2019-21},
+ NumLectures = {13},
+ NumStudents = {10},
+ Role = {ta},
+}
diff --git a/macros/latex/contrib/biblatex-contrib/biblatex-cv/doc/biblatex-cv.pdf b/macros/latex/contrib/biblatex-contrib/biblatex-cv/doc/biblatex-cv.pdf
new file mode 100644
index 0000000000..6e7a8eb1b4
--- /dev/null
+++ b/macros/latex/contrib/biblatex-contrib/biblatex-cv/doc/biblatex-cv.pdf
Binary files differ
diff --git a/macros/latex/contrib/biblatex-contrib/biblatex-cv/doc/biblatex-cv.tex b/macros/latex/contrib/biblatex-contrib/biblatex-cv/doc/biblatex-cv.tex
new file mode 100644
index 0000000000..0417630b45
--- /dev/null
+++ b/macros/latex/contrib/biblatex-contrib/biblatex-cv/doc/biblatex-cv.tex
@@ -0,0 +1,416 @@
+% The header to this document is based on the biblatex.tex document. The ordering and style of the documentation also is based on biblatex.tex.
+\documentclass{ltxdockit}[2011/03/25]
+\usepackage{btxdockit}
+\usepackage{fontspec}
+\usepackage{hyperref}
+\usepackage{zref-xr}
+
+\setmonofont{Courier New}
+\setmainfont[Ligatures=TeX]{Linux Libertine O}
+\setsansfont[Ligatures=TeX]{Linux Biolinum O}
+\usepackage[american]{babel}
+\usepackage[strict]{csquotes}
+\usepackage{tabularx}
+\usepackage{longtable}
+\usepackage{booktabs}
+\usepackage{shortvrb}
+\usepackage{pifont}
+\usepackage{microtype}
+\usepackage{typearea}
+\usepackage{mdframed}
+\areaset[current]{370pt}{700pt}
+\lstset{
+ basicstyle=\ttfamily,
+ keepspaces=true,
+ upquote=true,
+ frame=single,
+ breaklines=true,
+ postbreak=\raisebox{0ex}[0ex][0ex]{\ensuremath{\color{red}\hookrightarrow\space}}
+}
+\KOMAoptions{numbers=noenddot}
+\addtokomafont{title}{\sffamily}
+\addtokomafont{disposition}{\spotcolor}
+\addtokomafont{descriptionlabel}{\spotcolor}
+\setkomafont{caption}{\bfseries\sffamily\spotcolor}
+\setkomafont{captionlabel}{\bfseries\sffamily\spotcolor}
+\pretocmd{\cmd}{\sloppy}{}{}
+\pretocmd{\bibfield}{\sloppy}{}{}
+\pretocmd{\bibtype}{\sloppy}{}{}
+\makeatletter
+\RedeclareSectionCommand[%
+ beforeskip=-3.25ex\@plus -1ex \@minus -.2ex,%
+ afterskip=1.5ex \@plus .2ex,%
+]{paragraph}
+\RedeclareSectionCommand[%
+ beforeskip=-3.25ex\@plus -1ex \@minus -.2ex,%
+ indent=\z@,%
+]{subparagraph}
+\makeatother
+
+\MakeAutoQuote{«}{»}
+\MakeAutoQuote*{<}{>}
+
+% DES: Moved turning \| into a shorthand for verbatim material until later since it breaks something in the biblatex package
+
+\newcommand*{\allowbreakhere}{\discretionary{}{}{}}
+
+\newcommand*{\biber}{\sty{biber}\xspace}
+\newcommand*{\biblatex}{\sty{biblatex}\xspace}
+\newcommand*{\biblatexml}{\sty{biblatexml}\xspace}
+\newcommand*{\biblatexhome}{http://sourceforge.net/projects/biblatex/}
+\newcommand*{\biblatexctan}{http://ctan.org/pkg/biblatex/}
+
+% DES: Moved \titlepage and \hypersetup to the end of the preamble
+
+% tables
+
+\newcolumntype{H}{>{\sffamily\bfseries\spotcolor}l}
+\newcolumntype{L}{>{\raggedright\let\\=\tabularnewline}p}
+\newcolumntype{R}{>{\raggedleft\let\\=\tabularnewline}p}
+\newcolumntype{C}{>{\centering\let\\=\tabularnewline}p}
+\newcolumntype{V}{>{\raggedright\let\\=\tabularnewline\ttfamily}p}
+
+\newcommand*{\sorttablesetup}{%
+ \tablesetup
+ \ttfamily
+ \def\new{\makebox[1.25em][r]{\ensuremath\rightarrow}\,}%
+ \def\alt{\par\makebox[1.25em][r]{\ensuremath\hookrightarrow}\,}%
+ \def\note##1{\textrm{##1}}}
+
+\newcommand{\tickmarkyes}{\Pisymbol{psy}{183}}
+\newcommand{\tickmarkno}{\textendash}
+\providecommand*{\textln}[1]{#1}
+\providecommand*{\lnstyle}{}
+
+% markup and misc
+
+\setcounter{secnumdepth}{4}
+
+\makeatletter
+
+\newenvironment{nameparts}
+ {\trivlist\item
+ \tabular{@{}ll@{}}}
+ {\endtabular\endtrivlist}
+
+\newenvironment{namedelims}
+ {\trivlist\item
+ \tabularx{\textwidth}{@{}c@{=}l>{\raggedright\let\\=\tabularnewline}X@{}}}
+ {\endtabularx\endtrivlist}
+
+\newenvironment{namesample}
+ {\def\delim##1##2{\@delim{##1}{\normalfont\tiny\bfseries##2}}%
+ \def\@delim##1##2{{%
+ \setbox\@tempboxa\hbox{##1}%
+ \@tempdima=\wd\@tempboxa
+ \wd\@tempboxa=\z@
+ \box\@tempboxa
+ \begingroup\spotcolor
+ \setbox\@tempboxa\hb@xt@\@tempdima{\hss##2\hss}%
+ \vrule\lower1.25ex\box\@tempboxa
+ \endgroup}}%
+ \ttfamily\trivlist
+ \setlength\itemsep{0.5\baselineskip}}
+ {\endtrivlist}
+
+\makeatother
+
+\newrobustcmd*{\Deprecated}{%
+ \textcolor{spot}{\margnotefont Deprecated}}
+\newrobustcmd*{\DeprecatedMark}{%
+ \leavevmode\marginpar{\Deprecated}}
+\newrobustcmd*{\LF}{%
+ \textcolor{spot}{\margnotefont Label field}}
+\newrobustcmd*{\LFMark}{%
+ \leavevmode\marginpar{\LF}}
+\newrobustcmd*{\CSdelim}{%
+ \textcolor{spot}{\margnotefont Context Sensitive}}
+\newrobustcmd*{\CSdelimMark}{%
+ \leavevmode\marginpar{\CSdelim}}
+
+
+\newcommand*{\seestyleexample}[1]{%
+ \leavevmode
+ \marginpar{%
+ \raggedright
+ \footnotesize
+ Style example:\\
+ \href{file:examples/#1.pdf}{local},
+ \href{http://mirrors.ctan.org/macros/latex/contrib/biblatex/doc/examples/#1.pdf}
+ {online}.}%
+ \ignorespaces}
+
+% following snippet is based on code by Michael Ummels (TeX Stack Exchange)
+% <http://tex.stackexchange.com/a/13073/8305>
+\makeatletter
+ \newcommand\fnurl@[1]{\footnote{\url@{#1}}}
+ \DeclareRobustCommand{\fnurl}{\hyper@normalise\fnurl@}
+\makeatother
+
+\hyphenation{%
+ star-red
+ bib-lio-gra-phy
+ white-space
+}
+
+% DES: Begin additions/changes to biblatex.tex preamble
+\titlepage{%
+ title={Bib\LaTeX\ Style for Creating a Curriculum Vitae},
+ subtitle={Datatypes and Formatting Macros},
+ url={https://github.com/danielshub/biblatex-cv/},
+ author={Daniel E. Shub},
+ email={daniel.e.shub@gmail.com},
+ revision={0.01},
+ date={\today}%
+}
+
+\hypersetup{%
+ pdftitle={The biblatex-cv manual},
+ pdfsubject={Bib\LaTeX\ Style for Creating a Curriculum Vitae},
+ pdfauthor={Daniel E. Shub},
+ pdfkeywords={latex, biblatex, cv, style}
+}
+
+\usepackage{biblatex-cv}
+\highlightname{Doe}{Jon}{}{}
+\addbibresource{biblatex-cv.bib}
+
+\newcommand{\exampitem}[1]{\begingroup%
+ \par\itemsep0.5\itemsep\item%
+ Default Output: \par%
+ \printbibliography[env = unnumbered, resetnumbers = true, check = #1]%
+\endgroup}
+
+\MakeShortVerb{\|}
+
+\begin{document}
+\nocite{*}
+\printtitlepage
+\tableofcontents
+
+\section{Introduction}
+\subsection{About}
+This package is a \biblatex\ style for creating an academic curriculum vitae (CV) from a \bibtex\ \file{.bib} file using \biber\slash\biblatex. In addition to the standard publication types, an academic CV contains information about funding, teaching, mentoring, and other accomplishments. Depending on the purpose of the CV, the grouping of the items within a category changes. For example, when applying for jobs, it is often desirable to group teaching by whether it is at the undergraduate or graduate level (or maybe by class size), but when applying for promotion, you may need to group teaching depending on if the classes were inside/outside your department. The same goes for mentoring. In some cases it may make sense to group by the program/department the students/post-docs were enrolled and in other cases it may make sense to group undergraduate students separately. The purpose of this package is to harness the power of \biber\slash\biblatex\ to automatically group items on a CV and ensure consistent formatting of those items. The package implements a citation style (\file{biblatex-cv.cbx}), a references section style (\file{biblatex-cv.bbx}), a data model (\file{biblatex-cv.dbx}) that includes enhancements and additional entry types over the standard data model, and a string localization file (\file{american-cv.lbx}). It also includes a standard \latex\ package (\file{biblatex-cv.sty}) which provides useful interfaces for grouping items on the CV. Localization support is provided for CV specific strings in the accompanying \file{american-cv.lbx} file.
+
+The \sty{biblatex-cv} data model includes a number of additional entry types beyond those provided by the standard \biblatex\ data model. The \sty{biblatex-cv} data model, however, does not include all possible entry types that might appear on a CV. For example, it lacks an entry type related to employment, even though every CV likely includes employment history. The additional data types that are included in the \sty{biblatex-cv} data model were chosen based on the perceived likelihood that an individual might want to tailor the formatting, grouping, and/or ordering of the items of a particular entry type for a particular purpose. Future versions of the \sty{biblatex-cv} data model will likely include additional entry types and hopefully someday it will be possible to fully generate an academic CV directly from a \file{.bib} file.
+
+\subsection{License}
+Copyright \textcopyright\ 2020-- Daniel E. Shub. Permission is granted to copy, distribute and\slash or modify this software under the terms of the \lppl, version~1.3.\fnurl{http://www.latex-project.org/lppl.txt}. The current maintainer is Daniel Shub.
+
+\subsection{Feedback}
+Bug reports, comments and ideas are welcome. Please use the project page on GitHub to report bugs and submit feature requests.\fnurl{https://github.com/danielshub/biblatex-cv/}.
+
+\subsection{Acknowledgments}
+This package was motivated by the amazing improvements \bibtex\slash\biblatex\ provides over \bibtex\ for managing bibliographies. The data model and formatting macros provided by \biblatex\ allows a user with a good working knowledge of \latex\ to design new bibliography and citation styles. The \sty{biblatex-publist}\fnurl{https://github.com/jspitz/biblatex-publist} style provided a lot of inspiration of for this package and shows the flexiblity of \biblatex.
+
+\section{Use}
+This package is available at \url{https://github.com/danielshub/biblatex-cv/}. To install manually, you can download it and then put the \file{.cbx}, \file{.bbx}, \file{.lbx}, \file{.dbx}, and \file{.sty} files in your texmf tree (???). An example document is provided at the end of this document, but briefly, you specify the style in the usual way when loading \biblatex. While the style can be loaded just like any other \biblatex\ style, it is best to load it with |\usepackage{biblatex-cv}|. The \sty{biblatex-cv} package loads \biblatex\ with useful options as well as providing macros that are useful for grouping items in a CV. It can be loaded with:
+
+\begin{ltxcode}
+\usepackage[american]{babel}
+\usepackage{biblatex-cv}
+\end{ltxcode}
+
+\subsection{Package options}
+The verbosity of the output can be controlled by passing options to \sty{biblatex-cv}. These options include
+\begin{ltxcode}
+verbose-committee
+verbose-education
+verbose-teaching
+verbose-lists
+\end{ltxcode}
+
+\noindent The options |verbose-committee|, |verbose-education|, and |verbose-teaching| respectively control how verbose the lists of committees, education, and teaching are. The |verbose-lists| option is the same as passing |verbose-committee|, |verbose-education|, and |verbose-teaching|.
+
+\subsection{Package and Base Styles}
+ The citation style (\path{biblatex-cv.cbx}) is an unmodified version of the \biblatex\ |authoryear| citation style. The bibliography style (\path{biblatex-cv.bbx}) is based on the corresponding \biblatex\ |authoryear| style, but it patches a couple of the standard macros to provide additional functionality. The bibliography style essentially just adds bibliography drivers for the new entry types and provides a number of |toggles| to control the information that is presented. These |toggles| are in the form |cv@blx@ENTRYTYPE:FIELD|. The package (\path{biblatex-cv.sty}) provides macros for controlling the grouping and format of the items on the CV. The |\highlightname| macro is used to identify a particular name for special treatment. By default the name is made bold face in lists of authors and deleted from lists of presenters. The package also provides |numbered| and |unnumbered| bibliography environments. The numbers decrease in the |numbered| environment.
+
+\section{Database Guide}
+This package contains the following files:
+\begin{description}
+ \item[\path{american-cv.lbx}] The |biblatex-cv| localization files. This file provides language-specific macros for some fixed strings commonly used in academic CVs.
+ \item[\path{biblatex-cv.bbx}] The \sty{biblatex-cv} bibliography style. It is based on the standard \biblatex\ |authoryear| style but provides bibliography drivers for the new entry types and some minor modifications to the standard entry types.
+ \item[\path{biblatex-cv.bib}] The \file{.bib} entries used to provided the examples in this document.
+ \item[\path{biblatex-cv.cbx}] The \sty{biblatex-cv} citations style. It is just the standard \biblatex\ |authoryear| style.
+ \item[\path{biblatex-cv.dbx}] The \sty{biblatex-cv} data model. It is based on the standard \biblatex\ data model but provides enhancements and additional entry types to allow users to utilize more natural entry type and field names for certain entries.
+ \item[\path{biblatex-cv.sty}] A \latex\ package that provides macros for controlling the grouping and format of the items on the CV.
+ \item[\path{biblatex-cv.tex}] This document.
+ \item[\path{cv.tex}] An example CV populated by the entries in \path{biblatex-cv.bib}.
+\end{description}
+
+\subsection{New Entry Types}
+This section gives an overview of the novel entry types supported by the \sty{biblatex-cv} package data model along with the fields supported by each type. The lists below indicate the fields supported by each entry type. Note that the <required> fields are not strictly required in all cases. The fields marked as <optional> are optional in a technical sense. The \sty{biblatex-cv} package data model defines a few constraints for the format of certain fields. These are only validated against the data model with \biber's \opt{--validate-datamodel} option, however, the grouping features provided by the \sty{biblatex-cv} package assume the data model has been validated. Some generic fields like \bibfield{abstract} and \bibfield{annotation} or \bibfield{label} and \bibfield{shorthand} are not included in the lists below because they are independent of the entry type. See the \sty{biblatex-cv} data model specification in the file \file{biblatex-cv.dbx} for a complete specification.
+
+\begin{typelist}
+ \typeitem{abstract}
+ A published abstract in a journal, proceedings, or other location. While an abstract is often associated with a presentation, only a single entry should be made in the database since there is no way to automatically suppress the duplicate output. This entry type nearly identical to the \bibtype{article} entry type except it has the additional mandatory fields \bibfield{entrysubtype}, \bibfield{presentationtype}, and \bibfield{presenter}. The output format is also similar to that of the \bibtype{article} entry type. To work with the built in bibliography filters, the field \bibfield{entrysubtype} is constrained to be |poster| or |talk|.
+ \reqitem{author, title, journaltitle, year/date, entrysubtype, presentationtype, presenter}
+ \optitem{translator, annotator, commentator, subtitle, titleaddon, editor, editora, editorb, editorc, journalsubtitle, issuetitle, issuesubtitle, language, origlanguage, series, volume, number, eid, issue, month, pages, version, note, issn, addendum, pubstate, doi, eprint, eprintclass, eprinttype, url, urldate}
+ \exampitem{Abstract}
+
+ \typeitem{committee}
+ A committee entry is designed to reflect administrative service committees that have been served on. The field \bibfield{title} is the name of the committee. To work with the built in bibliography filters, the field \bibfield{entrysubtype} is constrained to be |departmental|, |external|, or |institutional|.
+ \reqitem{date, entrysubtype, title, institution}
+ \optitem{location, department, role, semesters}
+ \exampitem{Committee}
+
+ \typeitem{school}
+ A school is place where one studied and possibly earned a \bibtype{degree}. It is closely coupled with the \bibtype{degree} entry type.
+ \reqitem{date, institution, location}
+ \optitem{department, degreelist, gpa, honors}
+
+ \typeitem{degree}
+ A degree is something you earn at a \bibtype{school}. It is closely coupled with the \bibtype{school} entry type.
+ \reqitem{date, institution, location}
+ \optitem{department, degree, gpa, honors, major, minor, concentration, advisor, committee, title}
+ \exampitem{Education}
+
+ \typeitem{funding}
+ An entry type for recording funding and grants. To work with the built in bibliography filters, the field \bibfield{entrysubtype} is constrained to be |individual| or |institutional| and the field \bibfield{status} is constrained to be |completed|, |ongoing|, |pending|, |submitted|, or |unsuccessful|.
+ \reqitem{date, entrysubtype, status, author, funder}
+ \optitem{title, role, amount, currency, number, type}
+ \exampitem{Funding}
+
+ \typeitem{presentation}
+ A typically unpublished presentation from a conference or seminar, or other event. While a presentation is often associated with an abstract, only a single entry should be made in the database since there is no way to automatically suppress the duplicate output. To work with the built in bibliography filters, the field \bibfield{entrysubtype} is constrained to be |poster| or |talk|.
+ \reqitem{date, entrysubtype, author, presentationtype, presenter, title, institution, location}
+ \optitem{department}
+ \exampitem{Presentation}
+
+ \typeitem{student}
+ An entry type for students and other individuals that you hae been a mentor to or examined. To work with the built in bibliography filters, the field \bibfield{entrysubtype} is constrained to be |masters|, |phd|, |postdoc|, or |undergraduate| and the field \bibfield{role} is constrained to be |externalexaminer|, |internalexaminer|, |secondsupervisor|, or |supervisor|.
+ \reqitem{date, entrysubtype, role, name, institution, location}
+ \optitem{department}
+ \exampitem{Student}
+
+ \typeitem{teaching}
+ An entry type for classes that you have taught. To work with the built in bibliography filters, the field \bibfield{entrysubtype} is constrained to be |graduate| or |undergraduate|. The optional field \bibfield{classes} can be used in conjunction with the field \bibfield{crossref} when you have taught multiple sections of a particular class or have taught it over multiple years.
+ \reqitem{date, entrysubtype, title, department, institution, location}
+ \optitem{number, numlectures, numstudents, role, classes}
+ \exampitem{Teaching}
+
+\end{typelist}
+
+\subsection{Modified Entry Types}
+This section gives an overview of the modifications made to the standard entry types. Changes have been made to both the underlying data model and its constraints as well as the bibliography drivers and the underlying macros that format the output. The bibliography drivers for all entry types except the \bibtype{shorthand} and \bibtype{set} entry types, have been modified to not print the field \bibfield{note} in the standard location, but rather before the page reference and related type information. The field \bibfield{note} is now provided along with bibliometric information. The bibliometrics information is currently limited to the field \bibfield{numcites}.
+
+\begin{typelist}
+ \typeitem{article}
+ This is the standard \bibtype{article} entry type modified to have an additional mandatory field \bibfield{numcites}.
+ \typeitem{unpublished}
+ This is the standard \bibtype{unpublished} entry type modified to have an additional optional field \bibfield{journaltitle}.
+ \typeitem{unpublished}
+ This is the standard \bibtype{thesis} entry type but modified to work with the built in bibliography filters by constraining the field \bibfield{type} to be |doctoralthesis|, |mastersthesis|, or |undergradthesis|.
+\end{typelist}
+
+\subsection{New Entry Fields}
+\begin{fieldlist}
+ \listitem{advisor}{name} The advisor(s) for the entry type \bibtype{degree}.
+
+ \listitem{committee}{name} The committee member(s) for the entry type \bibtype{degree}.
+
+ \listitem{name}{name} The name of the student for the entry type \bibtype{student}. While technically a list, it is probably not advisable to use it as such and each \bibtype{student} entry should be for a single student.
+
+ \listitem{presenter}{name} The presenter(s) of a piece of work for the entry types \bibtype{abstract} and \bibtype{presentation}.
+
+ \listitem{concentration}{literal} A list of concentration areas for the entry type \bibtype{degree}.
+
+ \listitem{honors}{literal} A list of honors associated with the entry type \bibtype{degree}.
+
+ \listitem{minor}{literal} A list of minors associated with the entry type \bibtype{degree}.
+
+ \fielditem{amount}{integer} The amount of money for the entry type \bibtype{funding}.
+
+ \fielditem{numcites}{integer} The number of citations for a particular item.
+
+ \fielditem{numlectures}{integer} The number of lectures/classes for the entry type \bibtype{teaching}.
+
+ \fielditem{numstudents}{integer} The number of students in a class for the entry type \bibtype{teaching}.
+
+ \fielditem{currency}{literal} The currency of the field \bibfield{amount}.
+
+ \fielditem{degree}{literal} The degree earned for the entry type \bibtype{degree}.
+
+ \fielditem{department}{literal} An additional field that allows finer grain control of the \bibfield{institution} and \bibfield{location}. Unlike \bibfield{institution} and \bibfield{location}, \bibfield{department} is a field and not a list.
+
+ \fielditem{funder}{literal} The name of the funding institute for the entry type \bibtype{funding}.
+
+ \fielditem{gpa}{literal} The grade point average for the entry types \bibtype{degree} and \bibtype{school}.
+
+ \fielditem{major}{literal} The major for the entry type \bibtype{degree}.
+
+
+ \fielditem{presentationtype}{literal} The type of presentation for the entry types \bibtype{abstract} and \bibtype{presentation}. To work with the built in bibliography filters, the field should be constrained to be |contributed|, |internal|, |invited|, |job|, or |keynote|.
+
+ \fielditem{role}{key} The role (e.g., chair or secretary) that was served for entry type \bibtype{committee}.
+
+ \fielditem{status}{key} The status of the entry type \bibtype{funding}.
+
+ \fielditem{classes}{separated list of entrykeys} A comma separated list of keys to other \bibtype{teaching} entries. This can be useful if you taught a class multiple semesters and/or multiple sections of a class.
+
+ \fielditem{degreelist}{separated list of entrykeys} A comma separated list of \bibtype{degree} entries earned at the corresponding \bibtype{school}.
+
+ \fielditem{semesters}{separated list of entrykeys} a comma separated list of keys to other committee entries. This can be useful if you served on a committee for multiple years serving different roles.
+
+\end{fieldlist}
+
+\section{Example}
+The following example will create a boring looking CV. There is no reason you cannot make the formatting nicer. The purpose of this package is to control the sorting, grouping, and formatting of the items on the CV and not the CV itself.
+
+\begin{ltxcode}
+\documentclass{article}
+\usepackage[american]{babel}
+\usepackage{biblatex-cv}
+
+\highlightname{Doe}{Jon}{}{}
+\addbibresource{biblatex-cv.bib}
+
+\begin{document}
+ \nocite{*}
+ \section*{Education}
+ \printbibliography[env = unnumbered, resetnumbers = true, check = Education]
+
+ \section*{Funding}
+ \printbibliography[env = numbered, resetnumbers = true, check = Funding]
+
+ \section*{Abstracts}
+ \printbibliography[env = numbered, resetnumbers = true, check = Abstract]
+
+ \section*{Research Presentations}
+ \printbibliography[env = numbered, resetnumbers = true, check = Presentation]
+
+ \section*{Teaching}
+ \printbibliography[env = unnumbered, resetnumbers = true, check = Teaching]
+
+ \section*{Service}
+ \printbibliography[env = unnumbered, resetnumbers = true, check = Committee]
+
+ \section*{Mentoring}
+ \printbibliography[env = unnumbered, resetnumbers = true, check = Student]
+\end{document}
+\end{ltxcode}
+
+If instead of showing all your teaching together, you want to group by graduate and undergraduate teaching, that is as simply as changing |check = Teaching| to |check = Teaching:Graduate| and |check = Teaching:Undergraduate|. Grouping by the institute where the teaching was done is a little harder, since we do not know where you have taught. First you have to define the checks:
+\begin{ltxcode}
+\defbibcheck{Teaching:Oxford}{%
+ \ifentrytype{teaching}{}{\skipentry}%
+ \iffieldequalstr{institution}{Oxford}{}{\skipentry}%
+}
+\defbibcheck{Teaching:Cambridge}{%
+ \ifentrytype{teaching}{}{\skipentry}%
+ \iffieldequalstr{institution}{Cambridge}{}{\skipentry}%
+}
+\end{ltxcode}
+
+\noindent and then you can use |check = Teaching:Oxford| and |check = Teaching:Cambridge|. Of course if you group by university, you may not want to include the university in the reference. That can be controlled with |\togglefalse{cv@blx:teaching:institution}| and if you want to get rid of the department and location, there are toggles for that too. There are lots of checks and toggles predefined in \sty{biblatex-cv}. just look through the \file{biblatex-cv.sty} to see if what you need exists. If not, there is hopefully an example that will get you close.
+\end{document}
+
+% arara: clean: { extensions: [ aux, bbl, bcf, blg, out, run.xml, toc ] }
+% arara: clean: { extensions: [ log, pdf ] }
+% arara: xelatex: { options: [ '-halt-on-error' ], interaction: batchmode }
+% arara: biber: { options: [ '--validate_datamodel' ] }
+% arara: xelatex: { options: [ '-halt-on-error' ], interaction: batchmode }
+% arara: --> until !found('log', 'Rerun to get cross-references right.')
+% arara: --> && !found('log', 'There were undefined references.')
+% arara: clean: { extensions: [ aux, bbl, bcf, blg, out, run.xml, toc ] }
+% arara: clean: { extensions: [ log ] }
diff --git a/macros/latex/contrib/biblatex-contrib/biblatex-cv/doc/cv.pdf b/macros/latex/contrib/biblatex-contrib/biblatex-cv/doc/cv.pdf
new file mode 100644
index 0000000000..43a922c32c
--- /dev/null
+++ b/macros/latex/contrib/biblatex-contrib/biblatex-cv/doc/cv.pdf
Binary files differ
diff --git a/macros/latex/contrib/biblatex-contrib/biblatex-cv/doc/cv.tex b/macros/latex/contrib/biblatex-contrib/biblatex-cv/doc/cv.tex
new file mode 100644
index 0000000000..bb1fc0a10b
--- /dev/null
+++ b/macros/latex/contrib/biblatex-contrib/biblatex-cv/doc/cv.tex
@@ -0,0 +1,40 @@
+\documentclass{article}
+\usepackage[american]{babel}
+\usepackage{biblatex-cv}
+
+\highlightname{Doe}{Jon}{}{}
+\addbibresource{biblatex-cv.bib}
+
+\begin{document}
+ \nocite{*}
+ \section*{Education}
+ \printbibliography[env = unnumbered, resetnumbers = true, check = Education]
+
+ \section*{Funding}
+ \printbibliography[env = numbered, resetnumbers = true, check = Funding]
+
+ \section*{Abstracts}
+ \printbibliography[env = numbered, resetnumbers = true, check = Abstract]
+
+ \section*{Research Presentations}
+ \printbibliography[env = numbered, resetnumbers = true, check = Presentation]
+
+ \section*{Teaching}
+ \printbibliography[env = unnumbered, resetnumbers = true, check = Teaching]
+
+ \section*{Service}
+ \printbibliography[env = unnumbered, resetnumbers = true, check = Committee]
+
+ \section*{Mentoring}
+ \printbibliography[env = unnumbered, resetnumbers = true, check = Student]
+\end{document}
+
+% arara: clean: { extensions: [ aux, bbl, bcf, blg, out, run.xml, toc ] }
+% arara: clean: { extensions: [ log, pdf ] }
+% arara: pdflatex: { options: [ '-halt-on-error' ], interaction: batchmode }
+% arara: biber: { options: [ '--validate_datamodel' ] }
+% arara: pdflatex: { options: [ '-halt-on-error' ], interaction: batchmode }
+% arara: --> until !found('log', 'Rerun to get cross-references right.')
+% arara: --> && !found('log', 'There were undefined references.')
+% arara: clean: { extensions: [ aux, bbl, bcf, blg, out, run.xml, toc ] }
+% arara: clean: { extensions: [ log ] }
diff --git a/macros/latex/contrib/easybook/easybook.dtx b/macros/latex/contrib/easybook/easybook.dtx
index ad29040915..c89a99181f 100644
--- a/macros/latex/contrib/easybook/easybook.dtx
+++ b/macros/latex/contrib/easybook/easybook.dtx
@@ -23,11 +23,12 @@
%
% \changes{v1.20}{2021/01/09}{修改和完善用户文档。}
% \changes{v1.20}{2021/01/09}{模板更改为使用 CC-BY 许可协议。}
-% \changes{v1.25p}{2021/05/03}{取消附带的示例文件,可查看历史版本及其它来源的使用样例。}
+% \changes{v1.25q}{2021/05/03}{取消附带的示例文件,可查看历史版本及其它来源的使用样例。}
+% \changes{v1.25q}{2021/06/02}{将模块的代码独立出来为 \pkg{easybase} 宏包,适用于 \CTeX 文档类。}
%
% \title{\sffamily easybook 文档类手册}
% \author{瞿毅}
-% \date{2021/05/31\hskip\ccwd\relax v1.25p\thanks{\url{https://gitee.com/texl3/easybook}}}
+% \date{2021/06/02\hskip\ccwd\relax v1.25q\thanks{\url{https://gitee.com/texl3/easybook}}}
%
% \maketitle
% \begin{abstract}
@@ -57,6 +58,22 @@
% \end{document}
% \end{frameverb}
%
+% 或者直接使用 \pkg{easybase} 宏包,它的宏包选项有 \opt{class} 和 \opt{draft},包含了节 \ref{sec:interface options} 所示各个模块接口命令的功能,目前只适用于 \cls{ctexbook} 和 \cls{ctexart} 文档类。
+% \begin{frameverb}
+% \documentclass{ctexbook}
+%
+% \usepackage{easybase}
+% \ctexset{键值列表}
+%
+% \usepackage{宏包列表}
+%
+% \begin{document}
+% \chapter{欢迎}
+% \section{Welcome to easybook}
+% Hello, \LaTeX
+% \end{document}
+% \end{frameverb}
+%
% \subsection{编译方式}
% 假设您的 \TeX 源文件名为 \opt{easybook.tex},需要使用参考文献和索引功能,并使用 \hologo{XeLaTeX} 和 \hologo{BibTeX} 引擎编译文档,那么需要在命令行中执行
% \begin{frameverb}
@@ -117,7 +134,7 @@
% \end{syntax}
% 传递给 \cls{ctexbook} 设置字体,最好不要使用原有的 \opt{fontset} 选项,默认情况下使用自定义字体而不开启这个选项,可根据安装的字体及电脑系统选取。\opt{noto} 与 \opt{none} 选项的相关描述见小节 \ref{subsec:font settings},\opt{overleaf} 是一个各系统通用选项,会检测系统环境选取对应字体。
% \changes{v1.21}{2021/01/12}{默认字体与 \CTeX 一致,不再是自定义字体。}
-% \changes{v1.25p}{2021/05/04}{修复使用 \pkg{xeCJK} 与 \pkg{fontspec} 宏包的字体设置命令在 \hologo{LuaLaTeX} 下失效的问题。}
+% \changes{v1.25q}{2021/05/04}{修复使用 \pkg{xeCJK} 与 \pkg{fontspec} 宏包的字体设置命令在 \hologo{LuaLaTeX} 下失效的问题。}
% \end{function}
%
% \begin{function}[rEXP,added = 2021-01-05,updated = 2021-01-13]{paper}
@@ -140,11 +157,10 @@
% \begin{syntax}
% class = <(book)|article>
% \end{syntax}
-% 设置文类风格,默认为 \opt{book} 文档类。选用 \opt{article} 会变为 \opt{oneside} 单面排版,同时使文档编号、结构等与标准 \cls{article} 文档类的特性相似,可用于短篇幅的文章,不需要再使用 \tn{chapter} 命令,此时设置双栏目录的 \opt{style/multoc} 选项、小节 \ref{subsec:document command} 中打印目录命令的 \opt{multoc} 及 \opt{book} 选项均会失效,会以 \opt{section} 标题级别输出目录。
+% 设置文类风格,默认为 \opt{book} 文档类。选用 \opt{article} 会变为 \opt{oneside} 单面排版,同时使文档编号、结构等与标准 \cls{article} 文档类的特性相似,可用于短篇幅的文章,不需要再使用 \tn{chapter} 命令,此时设置双栏目录的 \opt{style/multoc} 选项、小节 \ref{subsec:document command} 中打印目录命令的 \opt{multoc} 及 \opt{book} 选项均会失效,会以 \opt{section} 标题级别输出目录。如果使用 \cls{ctexart} 配合 \pkg{easybase} 宏包,则 \opt{book} 宏包选项会失效。
% \changes{v1.23}{2021/03/16}{增加 article 文章模式,可用于短篇幅的文章。}
% \end{function}
%
-% \clearpage
% \subsection{文档命令}
% \label{subsec:document command}
% \subsubsection{文区命令}
@@ -160,7 +176,7 @@
% \end{function}
%
% \subsubsection{目录命令}
-% \begin{function}[added = 2021-02-20,updated = 2021-05-05]{\tableofcontents,\listoffigures,\listoftables,\listoflistings}
+% \begin{function}[added = 2021-02-20,updated = 2021-06-02]{\tableofcontents,\listoffigures,\listoftables,\listoflistings}
% \begin{syntax}
% \tn{tableofcontents+}\oarg{键值列表}\parg{附加代码}
% \tn{listoffigures*+}\oarg{键值列表}
@@ -168,22 +184,24 @@
% \tn{listoflistings*+}\oarg{键值列表}
% \end{syntax}
% \tn{listoffigures} 命令输出图片目录,\tn{listoftables} 输出表格目录,\tn{listoflistings} 输出代码目录。带有星号的目录命令不将标题添加进主文档目录,带有+号的目录命令可以使当前标题编号。\tn{tableofcontents+} 与其它命令不同是用于输出双语目录,需要与双语目录标题命令 \tn{bichapter} 等或设置图片、表格和代码等英文目录的标题配合使用,附加代码是中文与英文目录之间的代码,可以插入文本和换页命令等。目录的键值列表选项如下
-% \changes{v1.25p}{2021/05/05}{命令 \tn{tableofcontents} 可以输出双语目录。}
+% \changes{v1.25q}{2021/05/05}{命令 \tn{tableofcontents} 可以输出双语目录。}
% \end{function}
%
-% \begin{function}{multoc,title,format,aftertitle,pagestyle,book,article}
+% \begin{function}{multoc,columns,title,format,aftertitle,pagestyle,book,article}
% \begin{syntax}
% multoc = <true|(false)>
+% columns = <目录栏数|(2)>
% title = \{<中文目录标题>,<英文目录标题>\}
% format = <目录标题格式>
% aftertitle = <目录标题后的代码>
% pagestyle = <标题页的页面格式>
% \end{syntax}
-% \opt{multoc} 与 \opt{style/multoc} 选项具有相同的效果,但是具有优先性且可单独设置不同目录是否双栏排版。\opt{title} 选项用于更改目录的标题,标题之间用一个半角逗号分开,也可以只有一部分,表示中文目录的标题。当设置了英文目录标题时,英文目录标题将会自动加入主文档目录,与 \tn{tableofcontents+} 配合输出。
+% \opt{multoc} 与 \opt{style/multoc} 选项具有相同的效果,但是具有优先性且可单独设置不同目录是否多栏排版,默认为双栏,可使用 \opt{columns} 更改。\opt{title} 选项用于更改目录的标题,标题之间用一个半角逗号分开,也可以只有一部分,表示中文目录的标题。当设置了英文目录标题时,英文目录标题将会自动加入主文档目录,与 \tn{tableofcontents+} 配合输出。
%
% 在书籍模式且 \opt{book} 选项开启或者文章模式的情况下,\opt{format} 用于设置目录标题的格式,\opt{aftertitle} 选项的代码将被插入到目录标题内容之后,\opt{pagestyle} 是目录标题所在页的页面格式,除此之外还支持 \opt{ctexset/chapter} 或 \opt{ctexset/section} 所有的章节标题样式选项。\opt{book} 选项使目录标题按照 chapter 级别输出,\opt{article} 选项使目录标题按照 section 级别输出,默认为 chapter 级别。
% \changes{v1.23}{2021/03/15}{为几个目录增加键值列表的选项,可设置标题输出级别以及是否加入主目录。}
% \changes{v1.24}{2021/04/04}{目录命令的可选参数增加标题键值。}
+% \changes{v1.25q}{2021/06/02}{目录可设置多栏,并在 article 模式仍然生效。}
% \end{function}
%
% \subsubsection{标题命令}
@@ -199,7 +217,7 @@
% 双语目录的标题命令目前设置了常用的三个级别,它们将中文标题正常输出,并使中英文标题加入双语目录,这个功能是为国内一些大学的学位论文模板的要求设计,在一般情况和英文模式下不需要使用。
%
% 它们的星号变体命令使文中标题不编号,但是仍然加入双语目录,若英文标题参数空置那么仅加入中文目录而不加入英文目录,这一特点与 \tn{chapter*} 等命令的行为不太一样,在一些情况下会用到。可选参数是目录中的中文标题,默认与中文标题一致,正文标题过长时可用它设置在目录中显示的标题。双语目录的格式是一致的,共享使用 \tn{tocset} 的设置,英文目录的章级别标题名及编号的修改方式如下
-% \changes{v1.25p}{2021/05/05}{增加与双语目录配合的标题命令。}
+% \changes{v1.25q}{2021/05/05}{增加与双语目录配合的标题命令。}
% \end{function}
%
% \begin{function}{biname,binmuber}
@@ -272,7 +290,7 @@
% \changes{v1.10}{2021/01/08}{可以自定义主题颜色。}
% \end{function}
% \item[color-all] 统一设置主题的各部分颜色,参数是6个十六进制数字组成的 HTML 颜色代码,任意颜色的代码可以在常见的图片处理工具拾色器中查看。
-% \item[multoc] 开启双栏目录,可与 \opt{onecolumn} 和 \opt{twocolumn} 选项配合出单栏正文、单栏目录,双栏正文、单栏目录,单栏正文、双栏目录和双栏正文、双栏目录四种方式。
+% \item[multoc] 开启多栏目录,与小节 \ref{subsec:document command} 中目录命令的同名选项有相同效果。
% \item[withpart] 使章跟随每一部分编号,默认不跟随。
% \item[footwith] 使脚注跟随编号的计数器,默认跟随章,当开启新的一章时脚注编号从1开始。
% \item[rulewidth] 调整页眉、定理盒子以及代码框线条的粗细。
@@ -299,7 +317,7 @@
% \changes{v1.10}{2021/01/08}{修复了 \opt{linktopage} 选项关闭时目录中标题编号无超链接的问题。}
% \changes{v1.23}{2021/03/19}{修复在 \pkg{ctex} v2.5.6 更新后 \cls{easybook} 与 \pkg{hyperref} 冲突问题。}
% \changes{v1.24}{2021/04/05}{增加 \pkg{hyperref} 超链接选项完整的支持。}
-% \changes{v1.25p}{2021/05/06}{模块名 \opt{link} 改为 \opt{refset},\opt{link} 作为兼容性保留。}
+% \changes{v1.25q}{2021/05/06}{模块名 \opt{link} 改为 \opt{refset},\opt{link} 作为兼容性保留。}
% \end{function}
%
% \begin{optdesc}
@@ -356,7 +374,7 @@
% \ding{229} 以上选项路径为 \opt{subfont/...}
% \changes{v1.10}{2021/01/08}{加入自定义非正文字体选项。}
% \changes{v1.23}{2021/02/08}{修复使用 \tn{zihao} 命令设置字体失效的问题。}
-% \changes{v1.25p}{2021/05/31}{其它字体模块名由 \opt{elsefont} 改为 \opt{subfont},并增加数学字体的选项 \opt{subfont/math}。}
+% \changes{v1.25q}{2021/05/31}{其它字体模块名由 \opt{elsefont} 改为 \opt{subfont},并增加数学字体的选项 \opt{subfont/math}。}
% \end{function}
%
% \begin{optdesc}
@@ -381,7 +399,7 @@
% \changes{v1.21}{2021/01/11}{增加 \opt{bibset} 选项,可以使用 \hologo{BibTeX} 或 \hologo{biber} 编译参考文献。}
% \changes{v1.23}{2021/02/23}{使用 \opt{bibset} 选项时可设置标注风格。}
% \changes{v1.24}{2021/04/09}{可以不载入参考文献包手动配置参考文献格式。}
-% \changes{v1.25p}{2021/05/12}{取消 \opt{bib-loadpkg} 选项,由是否设置参考文献数据库来决定载入宏包。}
+% \changes{v1.25q}{2021/05/12}{取消 \opt{bib-loadpkg} 选项,由是否设置参考文献数据库来决定载入宏包。}
% \end{function}
%
% \begin{optdesc}
@@ -490,8 +508,10 @@
%
% \subsection{页面尺寸}
% \label{subsec:page margins}
-% \begin{function}[EXP,added = 2021-01-13,updated = 2021-05-11]{left,top,hmargin,textwidth,includehead}
+% \begin{function}[EXP,added = 2021-01-13,updated = 2021-06-02]{headruleskip,footruleskip,left,top,hmargin,textwidth,includehead}
% \begin{syntax}
+% headruleskip = <页眉线间距>
+% footruleskip = <页脚线间距>
% left = <页面左边距>
% top = <页面上边距>
% hmargin = \marg{左边距,右边距}
@@ -499,17 +519,18 @@
% includehead = <true|(false)>
% \end{syntax}
% \ding{229} 以上选项路径为 \opt{geoset/...} \\
-% 预置的页面尺寸与 \cls{Word} 的默认值接近,\opt{geoset} 会将选项交给 \pkg{geometry} 宏包接口命令 \tn{geometry} 处理,并且选项的键值列表与之相同,上面的选项仅是一小部分,支持的参数可查阅宏包帮助文档。当在此设置页面大小时,会覆盖 \opt{paper} 选项的设置。
+% 预置的页面尺寸与 \cls{Word} 的默认值接近,\opt{geoset} 会将前两个之外的选项交给 \pkg{geometry} 宏包接口命令 \tn{geometry} 处理,并且选项的键值列表与之相同,上面的选项仅是一小部分,支持的参数可查阅宏包帮助文档。当在此设置页面大小时,会覆盖 \opt{paper} 选项的设置。
% \changes{v1.21}{2021/01/13}{增加配置页面尺寸的选项。}
% \end{function}
%
% \begin{ctexexam}
% \geoset
% {
-% top = 2.5cm,
-% bottom = 2.5cm,
-% left = 3.2cm,
-% right = 3.2cm,
+% headruleskip = 3pt,
+% top = 2.5cm,
+% bottom = 2.5cm,
+% left = 3.2cm,
+% right = 3.2cm,
% ignoreall
% }
% \end{ctexexam}
@@ -584,7 +605,7 @@
% 上面几个选项用于设置脚注格式,\opt{fnparskip} 和 \opt{fnparindent} 选项仅在 \opt{fnhang = true} 时有效。\opt{fnhang} 用于将脚注设置为悬挂缩进的格式,\opt{fnmark-pos} 选项设置脚注编号的位置,正常文本或是上标形式。\opt{fnmark-num} 选项设置脚注编号的格式,默认为原生样式 \opt{plain},还可以改为 \opt{pifont} 的各个版本,从左往右依次为普通版、阴文衬线版、阳文无衬线版和阴文无衬线版,缺点是它们的编号仅包括1-10。
%
% \opt{fnmark-form} 是脚注标记的格式,在此设置字体命令会覆盖 \opt{subfont/footnote} 的设置,最后一个格式命令可以带有一个参数,这个参数接受脚注编号,一般配合 \opt{fnmark-num = plain} 使用。\opt{afterfnmark} 是脚注标记后的代码,一般用于设置标记与文本间的距离。
-% \changes{v1.25p}{2021/05/12}{增加脚注样式设置选项。}
+% \changes{v1.25q}{2021/05/12}{增加脚注样式设置选项。}
% \end{function}
%
% \subsection{目录样式}
@@ -603,7 +624,7 @@
% \opt{format} 是目录中标题的格式,可以设置字体和加入垂直间距等,垂直间距最好使用无间距重合的 \tn{addvspace} 命令,带有加号的 \opt{format+} 选项用于在已有的格式命令后附加内容。\opt{indent} 是目录中标题在页面左侧的缩进距离,应该有长度单位。\opt{rule} 选项中一般应当使用 \tn{tocrule} 命令,将会在接下来介绍。\opt{numsep} 是有编号的标题在目录中编号与标题的间距。
% \changes{v1.21}{2021/01/13}{增加配置目录样式的选项。}
% \changes{v1.24}{2021/04/20}{增加目录中编号与标题的间距选项 \opt{numsep}。}
-% \changes{v1.25p}{2021/05/14}{可以分别设置图片、表格和代码目录的格式。}
+% \changes{v1.25q}{2021/05/14}{可以分别设置图片、表格和代码目录的格式。}
% \end{function}
%
% \begin{function}[added = 2021-01-13,updated = 2021-04-20]{\tocrule,\EbNumberLine}
@@ -688,7 +709,7 @@
% \tn{end}\marg{环境名}
% \end{syntax}
% \pkg{tabularray} 是一个较新的宏包,不仅提供了 \pkg{tabularx}、\pkg{multirow}、和 \pkg{booktabs} 宏包的功能,并且不依赖其它宏包与它们同时使用不会产生冲突,还实现了用简便的键值列表方式设置表格样式。目前除了表头斜线以及跨页功能存在不足,其它方面已满足对表格绝大多数格式的需求,具体用法可见宏包的说明文档。
-% \changes{v1.25p}{2021/05/17}{使用功能更便捷齐全的 \pkg{tabularray} 宏包设置表格样式。}
+% \changes{v1.25q}{2021/05/17}{使用功能更便捷齐全的 \pkg{tabularray} 宏包设置表格样式。}
% \end{function}
%
% \subsection{列表}
@@ -852,7 +873,7 @@
% \tn{qtyrange}\oarg{键值列表}\marg{数字1}\marg{数字2}\marg{单位}
% \end{syntax}
% \pkg{siunitx} 宏包的 \tn{num} 命令可以输出以科学计数法表示的数值,单位格式化命令 \tn{unit} 包含文字项目(例如字母或数字)时,使用 . 和 \~{} 放置在单元乘积中间,并使用 \_ 和 \^{} 正确放置指定的下标和上标。格式化方法可在数学和文本模式下使用。而 \tn{qty} 命令可用来输出带有单位的量,\tn{qtyrange} 可以输出带有范围和单位的量。
-% \changes{v1.25p}{2021/05/18}{\pkg{siunitx} 宏包 v3.0.0 更新后用法改变,修改相关配置选项。}
+% \changes{v1.25q}{2021/05/18}{\pkg{siunitx} 宏包 v3.0.0 更新后用法改变,修改相关配置选项。}
% \end{function}
%
% \begin{ctexexam}
@@ -969,6 +990,7 @@
% \item[ulem] 提供可断行多样式的下划线命令。
% \item[geometry] 灵活完整的页面尺寸设置。
% \item[pdfpages] 在 \LaTeX 中插入 pdf 文档。
+% \item[multicol] 混合单栏和多栏排版。
% \item[graphicx] 增强了对图形插入的支持。
% \item[fancyhdr] 广泛控制页面的页眉和页脚。
% \item[titletoc] 设置目录中的标题格式。
@@ -988,42 +1010,8 @@
% \StopEventually{}
%<*class>
\NeedsTeXFormat{LaTeX2e}[2020/10/01]
-\RequirePackage{l3keys2e,etoolbox}
-\ProvidesExplClass{easybook}{2021/05/31}{1.25p}{beta}
-
-\cs_generate_variant:Nn \eb_ctex_define:nn { nx }
-\cs_generate_variant:Nn \int_to_arabic:n { v }
-\cs_generate_variant:Nn \dim_add:Nn { NV }
-\cs_generate_variant:Nn \eb_put_hyperref:n { x }
-\cs_generate_variant:Nn \seq_set_from_clist:Nn { No }
-\cs_new_protected:Npn \eb_ctex_define:nn #1
- { \keys_define:nn { ctex/#1 } }
-\cs_new_protected:Npn \eb_put_ctexbook:n #1
- { \PassOptionsToClass{#1}{ctexbook} }
-\cs_new_protected:Npn \eb_patch_cmd:Nnn #1#2#3
- {
- \ctex_patch_cmd_once:NnnnTF #1 { } {#2} {#3}
- { } { \ctex_patch_failure:N #1 }
- }
-\cs_new_protected:Npn \eb_preto_cmd:Nn #1#2
- {
- \ctex_preto_cmd:NnnTF #1 { } {#2}
- { } { \ctex_patch_failure:N #1 }
- }
-\cs_new_protected:Npn \eb_appto_cmd:Nn #1#2
- {
- \ctex_appto_cmd:NnnTF #1 { } {#2}
- { } { \ctex_patch_failure:N #1 }
- }
-\cs_new_protected:Npn \eb_at_begin_environment:nn #1#2
- {
- \seq_set_from_clist:No \l__eb_env_hook_name_seq {#1}
- \seq_map_inline:Nn \l__eb_env_hook_name_seq
- { \AtBeginEnvironment{##1}{#2} }
- }
-\cs_new_protected:Npn \eb_at_end_preamble:n
- { \ctex_gadd_ltxhook:nn { env/document/before } }
-\cs_gset_eq:NN \PackageWarning \use_none:nn
+\RequirePackage{l3keys2e}
+\ProvidesExplClass{easybook}{2021/06/02}{1.25q}{beta}
\bool_set_false:N \l__eb_compile_draft_bool
\bool_set_false:N \l__eb_title_newline_bool
@@ -1032,6 +1020,8 @@
\bool_set_true:N \l__eb_class_book_bool
\tl_new:N \g__eb_config_tl
\clist_new:N \g__eb_options_clist
+\cs_new_protected:Npn \eb_put_ctexbook:n #1
+ { \PassOptionsToClass{#1}{ctexbook} }
\keys_define:nn { easybook }
{
@@ -1087,6 +1077,249 @@
}
\ProcessKeysOptions{easybook}
+\eb_put_ctexbook:n { \g__eb_options_clist }
+\LoadClass[UTF8]{ctexbook}
+
+\RequirePackage{easybase}
+\ctex_at_end:n
+ {
+ \tl_if_empty:NF \g__eb_config_tl
+ { \file_input:n { \g__eb_config_tl } }
+ }
+
+\sys_if_engine_xetex:TF
+ {
+ \cs_new_eq:NN \eb_set_family:nnn \xeCJK_set_family:nnn
+ \cs_new_eq:NN \eb_switch_family:n \xeCJK_switch_family:n
+ }
+ {
+ \cs_new_eq:NN \eb_set_family:nnn \ctex_ltj_set_family:nnn
+ \cs_new_eq:NN \eb_switch_family:n \ctex_ltj_switch_family:n
+ }
+\cs_new_protected:Npn \eb_font_double_engine_set:n #1
+ {
+ \exp_args:Nc \RenewDocumentCommand {set#1font}{O{}mO{}}
+ {\use:c { eb_set#1font:nn } {##2} {##1,##3}}
+ }
+\seq_const_from_clist:Nn \c__eb_font_reset_family_seq
+ { main,sans,mono,CJKmain,CJKsans,CJKmono }
+\seq_map_inline:Nn \c__eb_font_reset_family_seq
+ { \eb_font_double_engine_set:n {#1} }
+
+\cs_new_protected:Npn \eb_setmainfont:nn #1#2
+ { \__fontspec_main_setmainfont:nn {#2} {#1} }
+\cs_new_protected:Npn \eb_setsansfont:nn #1#2
+ { \__fontspec_main_setsansfont:nn {#2} {#1} }
+\cs_new_protected:Npn \eb_setmonofont:nn #1#2
+ { \__fontspec_main_setmonofont:nn {#2} {#1} }
+\cs_new_protected:Npn \eb_setCJKmainfont:nn #1#2
+ { \eb_set_family:nnn { \CJKrmdefault } {#2} {#1} }
+\cs_new_protected:Npn \eb_setCJKsansfont:nn #1#2
+ { \eb_set_family:nnn { \CJKsfdefault } {#2} {#1} }
+\cs_new_protected:Npn \eb_setCJKmonofont:nn #1#2
+ { \eb_set_family:nnn { \CJKttdefault } {#2} {#1} }
+\cs_new_protected:Npn \eb_set_zhfont_songti:nn #1#2
+ { \eb_set_family:nnn { songti } {#2} {#1} }
+\cs_new_protected:Npn \eb_set_zhfont_heiti:nn #1#2
+ { \eb_set_family:nnn { heiti } {#2} {#1} }
+\cs_new_protected:Npn \eb_set_zhfont_kaishu:nn #1#2
+ { \eb_set_family:nnn { kaishu } {#2} {#1} }
+\cs_new_protected:Npn \eb_set_zhfont_fangsong:nn #1#2
+ { \eb_set_family:nnn { fangsong } {#2} {#1} }
+
+\tl_const:Nn \eb_option_zhfont_tl
+ { UprightFont = *,AutoFakeBold = 1.5,ItalicFont = * }
+\cs_new_protected:Npx \eb_setCJKmainfont:n #1
+ { \eb_setCJKmainfont:nn {#1} { \eb_option_zhfont_tl } }
+\cs_new_protected:Npx \eb_setCJKsansfont:n #1
+ { \eb_setCJKsansfont:nn {#1} { \eb_option_zhfont_tl } }
+\cs_new_protected:Npx \eb_setCJKmonofont:n #1
+ { \eb_setCJKmonofont:nn {#1} { \eb_option_zhfont_tl } }
+\cs_new_protected:Npx \eb_set_zhfont_songti:n #1
+ { \eb_set_zhfont_songti:nn {#1} { \eb_option_zhfont_tl } }
+\cs_new_protected:Npx \eb_set_zhfont_heiti:n #1
+ { \eb_set_zhfont_heiti:nn {#1} { \eb_option_zhfont_tl } }
+\cs_new_protected:Npx \eb_set_zhfont_kaishu:n #1
+ { \eb_set_zhfont_kaishu:nn {#1} { \eb_option_zhfont_tl } }
+\cs_new_protected:Npx \eb_set_zhfont_fangsong:n #1
+ { \eb_set_zhfont_fangsong:nn {#1} { \eb_option_zhfont_tl } }
+
+\bool_if:NT \l__eb_font_noto_bool
+ {
+ \eb_setmainfont:nn { NotoSerif }
+ {
+ Extension = .ttf,
+ UprightFont = *-Regular,
+ BoldFont = *-Bold,
+ ItalicFont = *-Italic
+ }
+ \eb_setsansfont:nn { NotoSans }
+ {
+ Extension = .ttf,
+ UprightFont = *-Regular,
+ BoldFont = *-Bold,
+ ItalicFont = *-Italic
+ }
+ \eb_setmonofont:nn { NotoSansMono }
+ {
+ Extension = .ttf,
+ UprightFont = *-Regular,
+ BoldFont = *-Bold
+ }
+ \eb_setCJKmainfont:nn { FZShuSong-Z01S }
+ {
+ UprightFont = *,
+ BoldFont = FZHei-B01S,
+ ItalicFont = FZKai-Z03S
+ }
+ \eb_setCJKsansfont:n { FZHei-B01S }
+ \eb_setCJKmonofont:n { FZFangSong-Z02S }
+ \eb_set_zhfont_songti:n { FZShuSong-Z01S }
+ \eb_set_zhfont_heiti:n { FZHei-B01S }
+ \eb_set_zhfont_kaishu:n { FZKai-Z03S }
+ \eb_set_zhfont_fangsong:n { FZFangSong-Z02S }
+ \cs_new_protected:Npn \songti { \eb_switch_family:n { songti } }
+ \cs_new_protected:Npn \heiti { \eb_switch_family:n { heiti } }
+ \cs_new_protected:Npn \kaishu { \eb_switch_family:n { kaishu } }
+ \cs_new_protected:Npn \fangsong { \eb_switch_family:n { fangsong } }
+ }
+
+\ctex_set:n
+ {
+ secnumdepth = 3,
+ tocdepth = \bool_if:NTF \l__eb_class_book_bool { 1 } { 2 },
+ part =
+ {
+ pagestyle = empty,
+ tocline = \EbNumberLine{#1}#2,
+ format = \color{ctex@frame}\sffamily\Huge,
+ aftername =
+ {
+ \bool_if:NTF \l__eb_title_newline_bool
+ { \par\nobreak } { \hspace{\ccwd} }
+ }
+ },
+ chapter =
+ {
+ % pagestyle = fancy,
+ format =
+ {
+ \color{ctex@frame}\sffamily\LARGE
+ \bool_if:NF \l__eb_title_newline_bool { \centering }
+ },
+ aftername =
+ {
+ \bool_if:NTF \l__eb_title_newline_bool
+ { \par\nobreak\vskip 1.5pc } { \hspace{\ccwd} }
+ },
+ beforeskip = -1.5ex,
+ afterskip = 3ex,
+ lotskip = 0.8pc,
+ lofskip = 0.8pc
+ },
+ section =
+ {
+ hang = true,
+ format = \color{ctex@frame}\sffamily\Large,
+ aftername = \hspace{0.5\ccwd},
+ beforeskip = 1.5ex plus .2ex minus .1ex,
+ afterskip = 1.5ex plus .2ex minus .1ex
+ },
+ subsection =
+ {
+ hang = true,
+ format = \color{ctex@frame}\sffamily\large,
+ aftername = \hspace{0.5\ccwd},
+ beforeskip = 1.3ex plus .2ex minus .1ex,
+ afterskip = 1.3ex plus .2ex minus .1ex
+ },
+ subsubsection =
+ {
+ hang = true,
+ format = \color{ctex@frame}\sffamily,
+ aftername = \hspace{0.5\ccwd},
+ beforeskip = \parskip,
+ afterskip = \parskip
+ }
+ }
+
+\bool_if:NF \l__eb_title_newline_bool
+ {
+ \ctex_set:n
+ {
+ part/hang = true,
+ chapter/hang = true
+ }
+ }
+%</class>
+%<*package>
+\NeedsTeXFormat{LaTeX2e}[2020/10/01]
+\RequirePackage{etoolbox}
+\ProvidesExplPackage{easybase}{2021/06/02}{1.25q}{beta}
+
+\cs_generate_variant:Nn \eb_ctex_define:nn { nx }
+\cs_generate_variant:Nn \int_to_arabic:n { v }
+\cs_generate_variant:Nn \dim_add:Nn { NV }
+\cs_generate_variant:Nn \eb_put_hyperref:n { x }
+\cs_generate_variant:Nn \seq_set_from_clist:Nn { No }
+\cs_generate_variant:Nn \int_compare:nNnT { oNnT }
+\cs_new_protected:Npn \eb_ctex_define:nn #1
+ { \keys_define:nn { ctex/#1 } }
+\cs_new_protected:Npn \eb_patch_cmd:Nnn #1#2#3
+ {
+ \ctex_patch_cmd_once:NnnnTF #1 { } {#2} {#3}
+ { } { \ctex_patch_failure:N #1 }
+ }
+\cs_new_protected:Npn \eb_preto_cmd:Nn #1#2
+ {
+ \ctex_preto_cmd:NnnTF #1 { } {#2}
+ { } { \ctex_patch_failure:N #1 }
+ }
+\cs_new_protected:Npn \eb_appto_cmd:Nn #1#2
+ {
+ \ctex_appto_cmd:NnnTF #1 { } {#2}
+ { } { \ctex_patch_failure:N #1 }
+ }
+\cs_new_protected:Npn \eb_at_begin_environment:nn #1#2
+ {
+ \seq_set_from_clist:No \l__eb_env_hook_name_seq {#1}
+ \seq_map_inline:Nn \l__eb_env_hook_name_seq
+ { \AtBeginEnvironment{##1}{#2} }
+ }
+\NewDocumentCommand{\eb@package@load}{O{}m}
+ {
+ \seq_set_from_clist:No \l__eb_package_name_seq {#2}
+ \int_compare:oNnT
+ { \seq_count:N \l__eb_package_name_seq } = { 1 }
+ { \PassOptionsToPackage{#1}{#2} }
+ \seq_map_inline:Nn \l__eb_package_name_seq
+ { \@ifpackageloaded{##1}{}{\RequirePackage{##1}} }
+ }
+\cs_new_protected:Npn \eb_at_end_preamble:n
+ { \ctex_gadd_ltxhook:nn { env/document/before } }
+\cs_gset_eq:NN \PackageWarning \use_none:nn
+
+\bool_if_exist:NF \l__eb_class_book_bool
+ { \bool_set_true:N \l__eb_class_book_bool }
+\bool_if_exist:NF \l__eb_page_twoside_bool
+ { \bool_set_true:N \l__eb_page_twoside_bool }
+\bool_if_exist:NF \l__eb_compile_draft_bool
+ { \bool_set_false:N \l__eb_compile_draft_bool }
+
+\keys_define:nn { easybase }
+ {
+ draft .code:n =
+ { \bool_set_true:N \l__eb_compile_draft_bool },
+
+ class .choice:,
+ class .value_required:n = true,
+ class/book .code:n = { },
+ class/article .code:n =
+ {
+ \bool_set_false:N \l__eb_class_book_bool
+ \bool_set_false:N \l__eb_page_twoside_bool
+ }
+ }
\clist_map_inline:nn
{
@@ -1098,13 +1331,12 @@
{ shortlabels,inline } { enumitem },
{ many } { tcolorbox },
{ xindy } { imakeidx },
- { CJKmath = true } { xeCJK },
+ { CJKmath } { xeCJK },
{ no-math } { fontspec },
{ upint } { newtxmath }
}
{ \PassOptionsToPackage #1 }
-\eb_put_ctexbook:n { \g__eb_options_clist }
-\LoadClass[UTF8]{ctexbook}
+\ProcessKeysOptions{easybase}
\bool_new:N \l__eb_multoc_bool
\bool_new:N \l__eb_uppercase_bool
@@ -1137,7 +1369,7 @@
withpart .bool_set:N = \l__eb_chap_withpart_bool,
withpart .default:n = true,
withpart .initial:n = false,
- rulewidth .dim_set:N = \l__eb_rule_width_dim,
+ rulewidth .dim_set:N = \eb@rule@width@dim,
rulewidth .initial:n = 0.75pt,
uppercase .bool_set:N = \l__eb_uppercase_bool,
uppercase .default:n = true,
@@ -1167,31 +1399,25 @@
number-sep .initial:n = { . }
}
-\RequirePackage
+\eb@package@load
{
xcolor,
footmisc,setspace,
amsmath,amsthm,thmtools,
chemformula,siunitx,ulem,pifont,
geometry,marginnote,nccfoots,
- pdfpages,graphicx,
+ pdfpages,graphicx,multicol,
fancyhdr,titletoc,caption,
tabularray,
enumitem,
listings
}
-\ctex_at_end:n
- {
- \tl_if_empty:NF \g__eb_config_tl
- { \file_input:n { \g__eb_config_tl } }
- }
-
\cs_new_protected:Npn \eb_package_later_bool:nn #1#2
{
\@ifpackagelater{#1}{#2}
- {}{\msg_warning:nnn { easybook } { package-old } {#1}}
+ {}{\msg_warning:nnn { easybase } { package-old } {#1}}
}
-\msg_new:nnn { easybook } { package-old }
+\msg_new:nnn { easybase } { package-old }
{
Package~#1~has~been~out~of~date. \\
Some~problems~or~errors~may~occur \\
@@ -1210,109 +1436,12 @@
}
{ \eb_package_later_bool:nn #1 }
-\sys_if_engine_xetex:TF
- {
- \cs_new_eq:NN \eb_set_family:nnn \xeCJK_set_family:nnn
- \cs_new_eq:NN \eb_switch_family:n \xeCJK_switch_family:n
- }
- {
- \cs_new_eq:NN \eb_set_family:nnn \ctex_ltj_set_family:nnn
- \cs_new_eq:NN \eb_switch_family:n \ctex_ltj_switch_family:n
- }
-\cs_new_protected:Npn \eb_font_double_engine_set:n #1
- {
- \exp_args:Nc \RenewDocumentCommand {set#1font}{O{}mO{}}
- {\use:c { eb_set#1font:nn } {##2} {##1,##3}}
- }
-\seq_const_from_clist:Nn \c__eb_font_reset_family_seq
- { main,sans,mono,CJKmain,CJKsans,CJKmono }
-\seq_map_inline:Nn \c__eb_font_reset_family_seq
- { \eb_font_double_engine_set:n {#1} }
-
-\cs_new_protected:Npn \eb_setmainfont:nn #1#2
- { \__fontspec_main_setmainfont:nn {#2} {#1} }
-\cs_new_protected:Npn \eb_setsansfont:nn #1#2
- { \__fontspec_main_setsansfont:nn {#2} {#1} }
-\cs_new_protected:Npn \eb_setmonofont:nn #1#2
- { \__fontspec_main_setmonofont:nn {#2} {#1} }
-\cs_new_protected:Npn \eb_setCJKmainfont:nn #1#2
- { \eb_set_family:nnn { \CJKrmdefault } {#2} {#1} }
-\cs_new_protected:Npn \eb_setCJKsansfont:nn #1#2
- { \eb_set_family:nnn { \CJKsfdefault } {#2} {#1} }
-\cs_new_protected:Npn \eb_setCJKmonofont:nn #1#2
- { \eb_set_family:nnn { \CJKttdefault } {#2} {#1} }
-\cs_new_protected:Npn \eb_set_zhfont_songti:nn #1#2
- { \eb_set_family:nnn { songti } {#2} {#1} }
-\cs_new_protected:Npn \eb_set_zhfont_heiti:nn #1#2
- { \eb_set_family:nnn { heiti } {#2} {#1} }
-\cs_new_protected:Npn \eb_set_zhfont_kaishu:nn #1#2
- { \eb_set_family:nnn { kaishu } {#2} {#1} }
-\cs_new_protected:Npn \eb_set_zhfont_fangsong:nn #1#2
- { \eb_set_family:nnn { fangsong } {#2} {#1} }
-
-\tl_const:Nn \eb_option_zhfont_tl
- { UprightFont = *,AutoFakeBold = 1.5,ItalicFont = * }
-\cs_new_protected:Npx \eb_setCJKmainfont:n #1
- { \eb_setCJKmainfont:nn {#1} { \eb_option_zhfont_tl } }
-\cs_new_protected:Npx \eb_setCJKsansfont:n #1
- { \eb_setCJKsansfont:nn {#1} { \eb_option_zhfont_tl } }
-\cs_new_protected:Npx \eb_setCJKmonofont:n #1
- { \eb_setCJKmonofont:nn {#1} { \eb_option_zhfont_tl } }
-\cs_new_protected:Npx \eb_set_zhfont_songti:n #1
- { \eb_set_zhfont_songti:nn {#1} { \eb_option_zhfont_tl } }
-\cs_new_protected:Npx \eb_set_zhfont_heiti:n #1
- { \eb_set_zhfont_heiti:nn {#1} { \eb_option_zhfont_tl } }
-\cs_new_protected:Npx \eb_set_zhfont_kaishu:n #1
- { \eb_set_zhfont_kaishu:nn {#1} { \eb_option_zhfont_tl } }
-\cs_new_protected:Npx \eb_set_zhfont_fangsong:n #1
- { \eb_set_zhfont_fangsong:nn {#1} { \eb_option_zhfont_tl } }
-
-\bool_if:NT \l__eb_font_noto_bool
- {
- \eb_setmainfont:nn { NotoSerif }
- {
- Extension = .ttf,
- UprightFont = *-Regular,
- BoldFont = *-Bold,
- ItalicFont = *-Italic
- }
- \eb_setsansfont:nn { NotoSans }
- {
- Extension = .ttf,
- UprightFont = *-Regular,
- BoldFont = *-Bold,
- ItalicFont = *-Italic
- }
- \eb_setmonofont:nn { NotoSansMono }
- {
- Extension = .ttf,
- UprightFont = *-Regular,
- BoldFont = *-Bold
- }
- \eb_setCJKmainfont:nn { FZShuSong-Z01S }
- {
- UprightFont = *,
- BoldFont = FZHei-B01S,
- ItalicFont = FZKai-Z03S
- }
- \eb_setCJKsansfont:n { FZHei-B01S }
- \eb_setCJKmonofont:n { FZFangSong-Z02S }
- \eb_set_zhfont_songti:n { FZShuSong-Z01S }
- \eb_set_zhfont_heiti:n { FZHei-B01S }
- \eb_set_zhfont_kaishu:n { FZKai-Z03S }
- \eb_set_zhfont_fangsong:n { FZFangSong-Z02S }
- \cs_new_protected:Npn \songti { \eb_switch_family:n { songti } }
- \cs_new_protected:Npn \heiti { \eb_switch_family:n { heiti } }
- \cs_new_protected:Npn \kaishu { \eb_switch_family:n { kaishu } }
- \cs_new_protected:Npn \fangsong { \eb_switch_family:n { fangsong } }
- }
-
\includepdfset{fitpaper = true}
\geometry
{
vmargin = 2.54cm,
hmargin = 3.17cm,
- columnsep = 2\ccwd,
+ columnsep = \ccwd,
headheight = 2.04cm,
headsep = 0.5cm,
footskip = 0.79cm,
@@ -1323,6 +1452,9 @@
\clist_new:N \g__eb_geometry_clist
\eb_ctex_define:nn { geoset }
{
+ headruleskip .tl_set:N = \headruleskip,
+ headruleskip .initial:n = 1.5pt,
+ footruleskip .tl_set:N = \footruleskip,
unknown .code:n =
{
\clist_gput_right:Nx \g__eb_geometry_clist
@@ -1342,13 +1474,13 @@
{ \hbox:n { }\thispagestyle{#1}\clearpage }
}
}
-\RenewDocumentCommand{\frontmatter}{sO{Roman}}
+\DeclareDocumentCommand{\frontmatter}{sO{Roman}}
{
\IfBooleanTF{#1}{\clearpage}{\cleardoublepage}
\boolfalse{@mainmatter}
\pagenumbering{#2}
}
-\RenewDocumentCommand{\mainmatter}{s}
+\DeclareDocumentCommand{\mainmatter}{s}
{
\IfBooleanTF{#1}{\clearpage}{\cleardoublepage}
\booltrue{@mainmatter}
@@ -1429,8 +1561,8 @@
headrule .initial:n =
{
\group_begin:
- \color{ctex@frame}\vspace*{1.5pt}
- \hrule width \textwidth height \l__eb_rule_width_dim
+ \color{ctex@frame}
+ \hrule\@width\textwidth\@height\eb@rule@width@dim
\group_end:
},
footrule .tl_gset:N = \footrule,
@@ -1440,7 +1572,7 @@
{
\group_begin:
\color{ctex@frame}
- \hrule width 0.35\columnwidth height \l__eb_rule_width_dim
+ \hrule\@width 0.35\columnwidth\@height\eb@rule@width@dim
\vspace*{2.6pt}
\group_end:
},
@@ -1674,7 +1806,7 @@
figure/#1 = ##1,
table/#1 = ##1,
lstlisting/#1 = ##1
- }
+ }
}
}
@@ -1686,6 +1818,12 @@
\hspace{\use:c { eb@toc@#1@numsep }}
}{}
}
+\cs_if_exist:NF \chapter
+ {
+ \newcounter{chapter}
+ \bool_set_false:N \l__eb_class_book_bool
+ \ctex_define:n { chapter/tocline .code:n = { } }
+ }
\newbool{eb@titletoc@hang@fix}
\eb_ctex_define:nn { tocset }
{
@@ -1863,29 +2001,32 @@
}
}
}
-
-\eb_appto_cmd:Nn \@chapter
+\cs_if_exist:NT \chapter
{
- \skip_if_eq:nnF { \l__eb_toc_lolskip_skip } { \c_zero_skip }
+ \eb_appto_cmd:Nn \@chapter
{
- \addtocontents{lol}
- {\protect\addvspace{\skip_use:N \l__eb_toc_lolskip_skip}}
+ \skip_if_eq:nnF { \l__eb_toc_lolskip_skip } { \c_zero_skip }
+ {
+ \addtocontents{lol}
+ {\protect\addvspace{\skip_use:N \l__eb_toc_lolskip_skip}}
+ }
+ \bool_if:NF \l__eb_class_book_bool
+ { \msg_error:nn { easybase } { no-chapter } }
+ }
+ \eb_appto_cmd:Nn \@schapter
+ {
+ \bool_if:NF \l__eb_class_book_bool
+ { \msg_error:nn { easybase } { no-chapter } }
}
- \bool_if:NF \l__eb_class_book_bool
- { \msg_error:nn { easybook } { no-chapter } }
- }
-\eb_appto_cmd:Nn \@schapter
- {
- \bool_if:NF \l__eb_class_book_bool
- { \msg_error:nn { easybook } { no-chapter } }
}
-\msg_new:nnn { easybook } { no-chapter }
+\msg_new:nnn { easybase } { no-chapter }
{
Your~document~class~is~in~article~mode. \\
The~\string\chapter\space~command~will~not~be~used.
}
-\tl_set:Nn \l__eb_toc_type_tl { book }
+\tl_set:Nx \l__eb_toc_type_tl
+ { \bool_if:NTF \l__eb_class_book_bool { book } { article } }
\tl_new:N \eb@bitoc@title
\keys_define:nn { eb/listoc }
{
@@ -1894,7 +2035,9 @@
multoc .bool_set:N = \l__eb_multoc_bool,
multoc .default:n = true,
multoc .initial:n = false,
- title .code:n = \eb_assign_toc_title:n {#1}
+ title .code:n = \eb_assign_toc_title:n {#1},
+ columns .int_set:N = \l__eb_toc_columns_int,
+ columns .initial:n = 2
}
\bool_if:NTF \l__eb_class_book_bool
{ \keys_define:nn { } { eb/listoc .inherit:n = ctex/chapter } }
@@ -1978,32 +2121,19 @@
}
\cs_new_protected:Npn \eb_multoc_pre_bool:
{
- \bool_if:NTF \l__eb_class_book_bool
- {
- \ifbool{@twocolumn}
- {
- \booltrue{@restonecol}
- \bool_if:NF \l__eb_multoc_bool { \onecolumn }
- }{
- \boolfalse{@restonecol}
- \bool_if:NT \l__eb_multoc_bool { \twocolumn }
- }
- }
- { \tl_set:Nn \l__eb_toc_type_tl { article } }
+ \bool_if:NT \l__eb_multoc_bool
+ { \begin{multicols}{\int_use:N \l__eb_toc_columns_int} }
}
\cs_new_protected:Npn \eb_multoc_post_bool:
- {
- \bool_if:NT \l__eb_class_book_bool
- { \ifbool{@restonecol}{\twocolumn}{\onecolumn} }
- }
+ { \bool_if:NT \l__eb_multoc_bool { \end{multicols} } }
\RenewDocumentCommand{\tableofcontents}{t+oD(){}}
{
\group_begin:
\tl_set_eq:NN \eb@toc@title \contentsname
\tl_set:Nn \eb@bitoc@title { Contents }
\IfValueT{#2}{\keys_set:nn { eb/listoc } {#2}}
- \eb_multoc_pre_bool:
\eb_title_mark_notoc:n { \eb@toc@title }
+ \eb_multoc_pre_bool:
\vspace*{\dim_use:N \l__eb_toc_offset_dim}
\@starttoc{toc}#3
\IfBooleanT{#1}
@@ -2020,8 +2150,8 @@
\group_begin:
\tl_set_eq:NN \eb@toc@title \listfigurename
\IfValueT{#3}{\keys_set:nn { eb/listoc } {#3}}
- \eb_multoc_pre_bool:
\eb_title_number_intoc_bool:nn {#2} {#1}
+ \eb_multoc_pre_bool:
\dim_add:NV \l__eb_toc_offset_dim \l__eb_toc_figure_offset_dim
\vspace*{\dim_use:N \l__eb_toc_offset_dim}
\@starttoc{lof}
@@ -2033,8 +2163,8 @@
\group_begin:
\tl_set_eq:NN \eb@toc@title \listtablename
\IfValueT{#3}{\keys_set:nn { eb/listoc } {#3}}
- \eb_multoc_pre_bool:
\eb_title_number_intoc_bool:nn {#2} {#1}
+ \eb_multoc_pre_bool:
\dim_add:NV \l__eb_toc_offset_dim \l__eb_toc_table_offset_dim
\vspace*{\dim_use:N \l__eb_toc_offset_dim}
\@starttoc{lot}
@@ -2046,8 +2176,8 @@
\group_begin:
\tl_set_eq:NN \eb@toc@title \lstlistlistingname
\IfValueT{#3}{\keys_set:nn { eb/listoc } {#3}}
- \eb_multoc_pre_bool:
\eb_title_number_intoc_bool:nn {#2} {#1}
+ \eb_multoc_pre_bool:
\dim_add:NV \l__eb_toc_offset_dim \l__eb_toc_lstlisting_offset_dim
\vspace*{\dim_use:N \l__eb_toc_offset_dim}
\@starttoc{lol}
@@ -2189,7 +2319,7 @@
\c__eb_subfont_math_charter_tl
{ \eb_put_newtxmath:n { charter,noOT1 } }
}
- { \RequirePackage{newtxmath,bm} }
+ { \eb@package@load{newtxmath,bm} }
}
\fp_new:N \l__eb_spread_line_fp
@@ -2438,6 +2568,7 @@
label = (\Alph*),
column-sep = \ccwd,
item-indent = 2\ccwd,
+ label-offset = 0.5\ccwd,
label-width = 1.5\ccwd,
after-item-skip = 0ex plus .1ex
}
@@ -2545,7 +2676,7 @@
boxsep = 0pt,
arc = 0pt,
fonttitle = \sffamily,
- boxrule = \l__eb_rule_width_dim
+ boxrule = \eb@rule@width@dim
}
\newtcbox[auto~counter]{\eb@exercise@box}[1]{tc-exercise,#1}
\NewDocumentEnvironment{outline}{O{}D(){\abstractname}o+b}
@@ -2632,7 +2763,7 @@
{
standard~jigsaw,
nobeforeafter,tcbox~raise~base,
- top = 1.25pt,bottom = 1.25pt,left = 1.25pt,right = 1.25pt,
+ top = 1.25qt,bottom = 1.25qt,left = 1.25qt,right = 1.25qt,
boxrule = 0.75pt,arc = 1pt,
colback = #1,opacityback = 0.12,colframe = #1,#3
}{#2}
@@ -2697,9 +2828,11 @@
\group_begin:
\tl_set_eq:NN \eb@toc@title \bibname
\IfValueT{##3}{ \keys_set:nn { eb/listoc } {##3} }
- \eb_multoc_pre_bool:
\tl_set:Nn \bibsection
- { \eb_title_number_intoc_bool:nn {##2} {##1} }
+ {
+ \eb_title_number_intoc_bool:nn {##2} {##1}
+ \eb_multoc_pre_bool:
+ }
\exp_args:NV \bibliography \l__eb_bib_datafile_tl
\eb_multoc_post_bool:
\group_end:
@@ -2735,12 +2868,12 @@
{
\bool_if:NTF \l__eb_backend_bibtex_bool
{
- \RequirePackage[sort&compress]{natbib}
+ \eb@package@load[sort&compress]{natbib}
\EbBibtexSet
}
{
\EbBiblatexPreset
- \RequirePackage{biblatex}
+ \eb@package@load{biblatex}
\EbBiblatexPostset
}
}
@@ -2799,7 +2932,7 @@
aboveskip = 1.5ex plus .2ex minus .1ex,
belowskip = 0.55ex plus .2ex minus .1ex,
keepspaces = true,
- framerule = \l__eb_rule_width_dim,
+ framerule = \eb@rule@width@dim,
columns = flexible,
abovecaptionskip = -1ex,
belowcaptionskip = 1.5ex
@@ -2931,7 +3064,7 @@
}
\ctex_at_end_preamble:n
{
- \RequirePackage{hyperref}
+ \eb@package@load{hyperref}
\urlstyle{same}
\eb_allow_urlbreak:
\ctex_hypersetup:n { pdfstartview = FitH }
@@ -2969,73 +3102,5 @@
\labelformat{section}{Section~#1}
\labelformat{subsection}{Subsection~#1}
}
-
-\ctex_set:n
- {
- secnumdepth = 3,
- tocdepth = \bool_if:NTF \l__eb_class_book_bool { 1 } { 2 },
- part =
- {
- pagestyle = empty,
- tocline = \EbNumberLine{#1}#2,
- format = \color{ctex@frame}\sffamily\Huge,
- aftername =
- {
- \bool_if:NTF \l__eb_title_newline_bool
- { \par\nobreak } { \hspace{\ccwd} }
- }
- },
- chapter =
- {
- % pagestyle = fancy,
- format =
- {
- \color{ctex@frame}\sffamily\LARGE
- \bool_if:NF \l__eb_title_newline_bool { \centering }
- },
- aftername =
- {
- \bool_if:NTF \l__eb_title_newline_bool
- { \par\nobreak\vskip 1.5pc } { \hspace{\ccwd} }
- },
- beforeskip = -1.5ex,
- afterskip = 3ex,
- lotskip = 0.8pc,
- lofskip = 0.8pc
- },
- section =
- {
- hang = true,
- format = \color{ctex@frame}\sffamily\Large,
- aftername = \hspace{0.5\ccwd},
- beforeskip = 1.5ex plus .2ex minus .1ex,
- afterskip = 1.5ex plus .2ex minus .1ex
- },
- subsection =
- {
- hang = true,
- format = \color{ctex@frame}\sffamily\large,
- aftername = \hspace{0.5\ccwd},
- beforeskip = 1.3ex plus .2ex minus .1ex,
- afterskip = 1.3ex plus .2ex minus .1ex
- },
- subsubsection =
- {
- hang = true,
- format = \color{ctex@frame}\sffamily,
- aftername = \hspace{0.5\ccwd},
- beforeskip = \parskip,
- afterskip = \parskip
- }
- }
-
-\bool_if:NF \l__eb_title_newline_bool
- {
- \ctex_set:n
- {
- part/hang = true,
- chapter/hang = true
- }
- }
-%</class>
+%</package>
\endinput \ No newline at end of file
diff --git a/macros/latex/contrib/easybook/easybook.ins b/macros/latex/contrib/easybook/easybook.ins
index af8f3f8134..b70169f22a 100644
--- a/macros/latex/contrib/easybook/easybook.ins
+++ b/macros/latex/contrib/easybook/easybook.ins
@@ -20,6 +20,10 @@ The latest version of this license is in
https://creativecommons.org/licenses/by/4.0/legalcode
\endpreamble
-\generate{\file{\jobname.cls}{\from{\jobname.dtx}{class}}}
+\generate
+ {
+ \file{easybook.cls}{\from{\jobname.dtx}{class}}
+ \file{easybase.sty}{\from{\jobname.dtx}{package}}
+ }
\endbatchfile \ No newline at end of file
diff --git a/macros/latex/contrib/easybook/easybook.pdf b/macros/latex/contrib/easybook/easybook.pdf
index 80221ec676..7d7ea10892 100644
--- a/macros/latex/contrib/easybook/easybook.pdf
+++ b/macros/latex/contrib/easybook/easybook.pdf
Binary files differ
diff --git a/macros/latex/required/babel/base/README.md b/macros/latex/required/babel/base/README.md
index bd9cd86eb0..f901605218 100644
--- a/macros/latex/required/babel/base/README.md
+++ b/macros/latex/required/babel/base/README.md
@@ -1,4 +1,4 @@
-## Babel 3.59
+## Babel 3.60
This package manages culturally-determined typographical (and other)
rules, and hyphenation patterns for a wide range of languages. Many
@@ -8,9 +8,9 @@ is a set of ini files for about 250 languages.
The latest stable version is available on <https://ctan.org/pkg/babel>.
-Changes in version 3.59 are described in:
+Changes in version 3.60 are described in:
-https://github.com/latex3/babel/blob/master/news-guides/news/whats-new-in-babel-3.59.md
+https://github.com/latex3/babel/blob/master/news-guides/news/whats-new-in-babel-3.60.md
Apart from the manual, you can find information on some aspects of babel at:
@@ -46,12 +46,12 @@ respective authors.
### Summary of Latest changes
```
-3.59 2021-05-16
- * Tentative kashida with user-definable rules (via transforms).
- * Fixes:
- - \babel@texpdf inconsistently defined (#130)
- - Partial fix for #131 (quotation with CJK).
- * Removed doubleletter.hyphen (Norsk), which serves to nothing.
+3.60 2021-06-02
+ * A major fix for a bug with the latest LaTeX releases: class
+ options were messed up.
+ * Improved justification=kashida/elongated: lists, vowelized texts.
+ * Fix: Underfull \hbox with amsmath (#133).
+ * Improved the ini file for Basque (captions).
```
### Previous changes
diff --git a/macros/latex/required/babel/base/babel.dtx b/macros/latex/required/babel/base/babel.dtx
index 76bb9a1f93..257ecd3851 100644
--- a/macros/latex/required/babel/base/babel.dtx
+++ b/macros/latex/required/babel/base/babel.dtx
@@ -20,6 +20,7 @@
%% This work has the LPPL maintenance status "maintained".
%%
%% The Current Maintainer of this work is Javier Bezos.
+
%%
%% The list of derived (unpacked) files belonging to the distribution
%% and covered by LPPL is defined by the unpacking scripts (with
@@ -31,7 +32,7 @@
%
% \iffalse
%<*filedriver>
-\ProvidesFile{babel.dtx}[2021/05/16 v3.59 The Babel package]
+\ProvidesFile{babel.dtx}[2021/06/02 v3.60 The Babel package]
\documentclass{ltxdoc}
\GetFileInfo{babel.dtx}
\usepackage{fontspec}
@@ -4967,8 +4968,8 @@ help from Bernd Raichle, for which I am grateful.
% \section{Tools}
%
% \begin{macrocode}
-%<<version=3.59>>
-%<<date=2021/05/16>>
+%<<version=3.60>>
+%<<date=2021/06/02>>
% \end{macrocode}
%
% \textbf{Do not use the following macros in \texttt{ldf} files. They
@@ -5435,14 +5436,14 @@ help from Bernd Raichle, for which I am grateful.
Reported}}
\def\@nolanerr#1{%
\bbl@error
- {You haven't defined the language #1\space yet.\\%
+ {You haven't defined the language '#1' yet.\\%
Perhaps you misspelled it or your installation\\%
is not complete}%
{Your command will be ignored, type <return> to proceed}}
\def\@nopatterns#1{%
\bbl@warning
{No hyphenation patterns were preloaded for\\%
- the language `#1' into the format.\\%
+ the language '#1' into the format.\\%
Please, configure your TeX system to add them and\\%
rebuild the format. Now I will use the patterns\\%
preloaded for \bbl@nulllanguage\space instead}}
@@ -5607,10 +5608,10 @@ help from Bernd Raichle, for which I am grateful.
\bbl@csarg\edef{opt@#1}{#2}%
\else
\bbl@error
- {Bad option `#1=#2'. Either you have misspelled the\\%
- key or there is a previous setting of `#1'. Valid\\%
- keys are, among others, `shorthands', `main', `bidi',\\%
- `strings', `config', `headfoot', `safe', `math'.}%
+ {Bad option '#1=#2'. Either you have misspelled the\\%
+ key or there is a previous setting of '#1'. Valid\\%
+ keys are, among others, 'shorthands', 'main', 'bidi',\\%
+ 'strings', 'config', 'headfoot', 'safe', 'math'.}%
{See the manual for further details.}
\fi}
% \end{macrocode}
@@ -6534,8 +6535,8 @@ help from Bernd Raichle, for which I am grateful.
\else
\ifnum\bbl@bidimode>100 \ifnum\bbl@bidimode<200
\bbl@error
- {The bidi method `basic' is available only in\\%
- luatex. I'll continue with `bidi=default', so\\%
+ {The bidi method 'basic' is available only in\\%
+ luatex. I'll continue with 'bidi=default', so\\%
expect wrong results}%
{See the manual for further details.}%
\let\bbl@beforeforeign\leavevmode
@@ -6661,16 +6662,20 @@ help from Bernd Raichle, for which I am grateful.
\def\bbl@dirparastext{\pardir\the\textdir\relax}% %%%%
% Sadly, we have to deal with boxes in math with basic.
% Activated every math with the package option bidi=:
- \def\bbl@mathboxdir{%
- \ifcase\bbl@thetextdir\relax
- \everyhbox{\textdir TLT\relax}%
- \else
- \everyhbox{\textdir TRT\relax}%
- \fi}
- \frozen@everymath\expandafter{%
- \expandafter\bbl@mathboxdir\the\frozen@everymath}
- \frozen@everydisplay\expandafter{%
- \expandafter\bbl@mathboxdir\the\frozen@everydisplay}
+ \ifnum\bbl@bidimode>\z@
+ \def\bbl@mathboxdir{%
+ \ifcase\bbl@thetextdir\relax
+ \everyhbox{\bbl@mathboxdir@aux L}%
+ \else
+ \everyhbox{\bbl@mathboxdir@aux R}%
+ \fi}
+ \def\bbl@mathboxdir@aux#1{%
+ \@ifnextchar\egroup{}{\textdir T#1T\relax}}
+ \frozen@everymath\expandafter{%
+ \expandafter\bbl@mathboxdir\the\frozen@everymath}
+ \frozen@everydisplay\expandafter{%
+ \expandafter\bbl@mathboxdir\the\frozen@everydisplay}
+ \fi
\else % pdftex=0, xetex=2
\newcount\bbl@dirlevel
\chardef\bbl@thetextdir\z@
@@ -6832,7 +6837,7 @@ help from Bernd Raichle, for which I am grateful.
\expandafter\let\expandafter\BabelModifiers
\csname bbl@mod@\CurrentOption\endcsname}%
{\bbl@error{%
- Unknown option `\CurrentOption'. Either you misspelled it\\%
+ Unknown option '\CurrentOption'. Either you misspelled it\\%
or the language definition file \CurrentOption.ldf was not found}{%
Valid options are, among others: shorthands=, KeepShorthandsActive,\\%
activeacute, activegrave, noconfigs, safe=, main=, math=\\%
@@ -6884,7 +6889,7 @@ help from Bernd Raichle, for which I am grateful.
* Local config file \bbl@opt@config.cfg used^^J%
*}}%
{\bbl@error{%
- Local config file `\bbl@opt@config.cfg' not found}{%
+ Local config file '\bbl@opt@config.cfg' not found}{%
Perhaps you misspelled it.}}%
\fi
% \end{macrocode}
@@ -6943,19 +6948,27 @@ help from Bernd Raichle, for which I am grateful.
\let\bbl@tempb\@nnil
\bbl@foreach\@classoptionslist{%
\bbl@ifunset{ds@#1}%
- {\IfFileExists{#1.ldf}{}%
- {\IfFileExists{babel-#1.tex}{}{\@namedef{ds@#1}{}}}}%
- {}%
- \bbl@ifunset{ds@#1}%
- {\def\bbl@tempb{#1}%
- \DeclareOption{#1}{%
- \ifnum\bbl@iniflag>\@ne
- \bbl@ldfinit
- \babelprovide[import]{#1}%
- \bbl@afterldf{}%
- \else
- \bbl@load@language{#1}%
- \fi}}%
+ {\IfFileExists{#1.ldf}%
+ {\def\bbl@tempb{#1}%
+ \DeclareOption{#1}{%
+ \ifnum\bbl@iniflag>\@ne
+ \bbl@ldfinit
+ \babelprovide[import]{#1}%
+ \bbl@afterldf{}%
+ \else
+ \bbl@load@language{#1}%
+ \fi}}%
+ {\IfFileExists{babel-#1.tex}% TODO. Copypaste pattern
+ {\def\bbl@tempb{#1}%
+ \DeclareOption{#1}{%
+ \ifnum\bbl@iniflag>\@ne
+ \bbl@ldfinit
+ \babelprovide[import]{#1}%
+ \bbl@afterldf{}%
+ \else
+ \bbl@load@language{#1}%
+ \fi}}%
+ {}}}%
{}}
% \end{macrocode}
%
@@ -7013,10 +7026,10 @@ help from Bernd Raichle, for which I am grateful.
\expandafter\bbl@tempa\bbl@loaded,\@nnil
\ifx\bbl@tempb\bbl@tempc\else
\bbl@warning{%
- Last declared language option is `\bbl@tempc',\\%
- but the last processed one was `\bbl@tempb'.\\%
- The main language cannot be set as both a global\\%
- and a package option. Use `main=\bbl@tempc' as\\%
+ Last declared language option is '\bbl@tempc',\\%
+ but the last processed one was '\bbl@tempb'.\\%
+ The main language can't be set as both a global\\%
+ and a package option. Use 'main=\bbl@tempc' as\\%
option. Reported}%
\fi
\else
@@ -7555,7 +7568,7 @@ help from Bernd Raichle, for which I am grateful.
\bbl@iflanguage\languagename{%
\expandafter\ifx\csname date\languagename\endcsname\relax
\bbl@error
- {Unknown language `\languagename'. Either you have\\%
+ {Unknown language '\languagename'. Either you have\\%
misspelled its name, it has not been installed,\\%
or you requested it in a previous run. Fix its name,\\%
install it or just rerun the file, respectively. In\\%
@@ -7832,7 +7845,7 @@ help from Bernd Raichle, for which I am grateful.
\bbl@iflanguage\languagename{%
\expandafter\ifx\csname date\languagename\endcsname\relax
\bbl@warning % TODO - why a warning, not an error?
- {Unknown language `#1'. Either you have\\%
+ {Unknown language '#1'. Either you have\\%
misspelled its name, it has not been installed,\\%
or you requested it in a previous run. Fix its name,\\%
install it or just rerun the file, respectively. In\\%
@@ -8089,14 +8102,14 @@ help from Bernd Raichle, for which I am grateful.
Reported}}
\def\@nolanerr#1{%
\bbl@error
- {You haven't defined the language #1\space yet.\\%
+ {You haven't defined the language '#1' yet.\\%
Perhaps you misspelled it or your installation\\%
is not complete}%
{Your command will be ignored, type <return> to proceed}}
\def\@nopatterns#1{%
\bbl@warning
{No hyphenation patterns were preloaded for\\%
- the language `#1' into the format.\\%
+ the language '#1' into the format.\\%
Please, configure your TeX system to add them and\\%
rebuild the format. Now I will use the patterns\\%
preloaded for \bbl@nulllanguage\space instead}}
@@ -9182,7 +9195,7 @@ help from Bernd Raichle, for which I am grateful.
% \begin{macrocode}
\def\@notshorthand#1{%
\bbl@error{%
- The character `\string #1' should be made a shorthand character;\\%
+ The character '\string #1' should be made a shorthand character;\\%
add the command \string\useshorthands\string{#1\string} to
the preamble.\\%
I will ignore your instruction}%
@@ -9225,7 +9238,7 @@ help from Bernd Raichle, for which I am grateful.
\ifx#2\@nnil\else
\bbl@ifunset{bbl@active@\string#2}%
{\bbl@error
- {I cannot switch `\string#2' on or off--not a shorthand}%
+ {I can't switch '\string#2' on or off--not a shorthand}%
{This character is not a shorthand. Maybe you made\\%
a typing mistake? I will ignore your instruction.}}%
{\ifcase#1% off, on, off*
@@ -10954,8 +10967,8 @@ help from Bernd Raichle, for which I am grateful.
% For bidi texts, to switch the font based on direction
\ifx\bbl@KVP@mapfont\@nil\else
\bbl@ifsamestring{\bbl@KVP@mapfont}{direction}{}%
- {\bbl@error{Option `\bbl@KVP@mapfont' unknown for\\%
- mapfont. Use `direction'.%
+ {\bbl@error{Option '\bbl@KVP@mapfont' unknown for\\%
+ mapfont. Use 'direction'.%
{See the manual for details.}}}%
\bbl@ifunset{bbl@lsys@\languagename}{\bbl@provide@lsys{\languagename}}{}%
\bbl@ifunset{bbl@wdir@\languagename}{\bbl@provide@dirs{\languagename}}{}%
@@ -13770,6 +13783,8 @@ help from Bernd Raichle, for which I am grateful.
Babel.arabic.elong_map[\the\localeid] = {}
luatexbase.add_to_callback('post_linebreak_filter',
Babel.arabic.justify, 'Babel.arabic.justify')
+% luatexbase.add_to_callback('hpack_filter',
+% Babel.arabic.justify_hbox, 'Babel.arabic.justify_hbox')
}}%
% Save both node lists to make replacement. TODO. Save also widths to
% make computations
@@ -13840,112 +13855,134 @@ Babel.arabic.justify_enabled = true
function Babel.arabic.justify(head)
if not Babel.arabic.justify_enabled then return head end
+ for line in node.traverse_id(node.id'hlist', head) do
+ Babel.arabic.justify_hlist(head, line)
+ end
+ return head
+end
+
+function Babel.arabic.justify_hbox(head, gc, size, pack)
+ if Babel.arabic.justify_enabled and pack == 'exactly' then
+ Babel.arabic.justify_hlist(head, nil, gc, size, pack)
+ end
+ return head
+end
+
+function Babel.arabic.justify_hlist(head, line, gc, size, pack)
local d, new
local k_list, k_item, pos_inline
- local width, width_new, full, k_curr, wt_pos, goal
+ local width, width_new, full, k_curr, wt_pos, goal, shift
local subst_done = false
local elong_map = Babel.arabic.elong_map
local last_line
local GLYPH = node.id'glyph'
local KASHIDA = luatexbase.registernumber'bblar@kashida'
local LOCALE = luatexbase.registernumber'bbl@attr@locale'
+
+ if line == nil then
+ line = {}
+ line.glue_sign = 1
+ line.glue_order = 0
+ line.head = head
+ line.shift = 0
+ line.width = size
+ end
- for line in node.traverse_id(node.id'hlist', head) do
- % Exclude last line. todo. But-- it discards one-word lines, too!
- % ? Look for glue = 12:15
- if (line.glue_sign == 1 and line.glue_order == 0) then
- elongs = {} % Stores elongated candidates of each line
- k_list = {} % And all letters with kashida
- pos_inline = 0 % Not yet used
-
- for n in node.traverse_id(GLYPH, line.head) do
- pos_inline = pos_inline + 1 % To find where it is. Not used.
-
- % Elongated glyphs
- if elong_map then
- local locale = node.get_attribute(n, LOCALE)
- if elong_map[locale] and elong_map[locale][n.font] and
- elong_map[locale][n.font][n.char] then
- table.insert(elongs, {node = n, locale = locale} )
- node.set_attribute(n.prev, KASHIDA, 0)
- end
+ % Exclude last line. todo. But-- it discards one-word lines, too!
+ % ? Look for glue = 12:15
+ if (line.glue_sign == 1 and line.glue_order == 0) then
+ elongs = {} % Stores elongated candidates of each line
+ k_list = {} % And all letters with kashida
+ pos_inline = 0 % Not yet used
+
+ for n in node.traverse_id(GLYPH, line.head) do
+ pos_inline = pos_inline + 1 % To find where it is. Not used.
+
+ % Elongated glyphs
+ if elong_map then
+ local locale = node.get_attribute(n, LOCALE)
+ if elong_map[locale] and elong_map[locale][n.font] and
+ elong_map[locale][n.font][n.char] then
+ table.insert(elongs, {node = n, locale = locale} )
+ node.set_attribute(n.prev, KASHIDA, 0)
end
+ end
- % Tatwil
- if Babel.kashida_wts then
- local k_wt = node.get_attribute(n, KASHIDA)
- if k_wt > 0 then % todo. parameter for multi inserts
- table.insert(k_list, {node = n, weight = k_wt, pos = pos_inline})
- end
+ % Tatwil
+ if Babel.kashida_wts then
+ local k_wt = node.get_attribute(n, KASHIDA)
+ if k_wt > 0 then % todo. parameter for multi inserts
+ table.insert(k_list, {node = n, weight = k_wt, pos = pos_inline})
end
-
- end % of node.traverse_id
-
- if #elongs == 0 and #k_list == 0 then goto next_line end
-
- full = line.width
- goal = full * Babel.arabic.justify_factor % A bit crude
- width = node.dimensions(line.head) % The 'natural' width
-
- % == Elongated ==
- % Original idea taken from 'chikenize'
- while (#elongs > 0 and width < goal) do
- subst_done = true
- local x = #elongs
- local curr = elongs[x].node
- local oldchar = curr.char
- curr.char = elong_map[elongs[x].locale][curr.font][curr.char]
- width = node.dimensions(line.head) % Check if the line is too wide
- % Substitute back if the line would be too wide and break:
- if width > goal then
- curr.char = oldchar
- break
- end
- % If continue, pop the just substituted node from the list:
- table.remove(elongs, x)
end
- % == Tatwil ==
- if #k_list == 0 then goto next_line end
-
- width = node.dimensions(line.head) % The 'natural' width
- k_curr = #k_list
- wt_pos = 1
-
- while width < goal do
- subst_done = true
- k_item = k_list[k_curr].node
- if k_list[k_curr].weight == Babel.kashida_wts[wt_pos] then
- d = node.copy(k_item)
- d.char = 0x0640
- line.head, new = node.insert_after(line.head, k_item, d)
- width_new = node.dimensions(line.head)
- if width > goal or width == width_new then
- node.remove(line.head, new) % Better compute before
- break
- end
- width = width_new
- end
- if k_curr == 1 then
- k_curr = #k_list
- wt_pos = (wt_pos >= table.getn(Babel.kashida_wts)) and 1 or wt_pos+1
- else
- k_curr = k_curr - 1
+ end % of node.traverse_id
+
+ if #elongs == 0 and #k_list == 0 then goto next_line end
+ full = line.width
+ shift = line.shift
+ goal = full * Babel.arabic.justify_factor % A bit crude
+ width = node.dimensions(line.head) % The 'natural' width
+
+ % == Elongated ==
+ % Original idea taken from 'chikenize'
+ while (#elongs > 0 and width < goal) do
+ subst_done = true
+ local x = #elongs
+ local curr = elongs[x].node
+ local oldchar = curr.char
+ curr.char = elong_map[elongs[x].locale][curr.font][curr.char]
+ width = node.dimensions(line.head) % Check if the line is too wide
+ % Substitute back if the line would be too wide and break:
+ if width > goal then
+ curr.char = oldchar
+ break
+ end
+ % If continue, pop the just substituted node from the list:
+ table.remove(elongs, x)
+ end
+
+ % == Tatwil ==
+ if #k_list == 0 then goto next_line end
+
+ width = node.dimensions(line.head) % The 'natural' width
+ k_curr = #k_list
+ wt_pos = 1
+
+ while width < goal do
+ subst_done = true
+ k_item = k_list[k_curr].node
+ if k_list[k_curr].weight == Babel.kashida_wts[wt_pos] then
+ d = node.copy(k_item)
+ d.char = 0x0640
+ line.head, new = node.insert_after(line.head, k_item, d)
+ width_new = node.dimensions(line.head)
+ if width > goal or width == width_new then
+ node.remove(line.head, new) % Better compute before
+ break
end
+ width = width_new
end
+ if k_curr == 1 then
+ k_curr = #k_list
+ wt_pos = (wt_pos >= table.getn(Babel.kashida_wts)) and 1 or wt_pos+1
+ else
+ k_curr = k_curr - 1
+ end
+ end
- ::next_line::
+ ::next_line::
- % Must take into account marks and ins, see luatex manual.
- % Have to be executed only if there are changes. Investigate
- % what's going on exactly.
- if subst_done then
- node.insert_before(head, line, node.hpack(line.head, full, 'exactly'))
- node.remove(head, line)
- end
- end % if process line
- end % for lines
- return head
+ % Must take into account marks and ins, see luatex manual.
+ % Have to be executed only if there are changes. Investigate
+ % what's going on exactly.
+ if subst_done and not gc then
+ d = node.hpack(line.head, full, 'exactly')
+ d.shift = shift
+ node.insert_before(head, line, d)
+ node.remove(head, line)
+ end
+ end % if process line
end
}
\endgroup
diff --git a/macros/latex/required/babel/base/babel.ins b/macros/latex/required/babel/base/babel.ins
index 064fd5a50c..448f711c9e 100644
--- a/macros/latex/required/babel/base/babel.ins
+++ b/macros/latex/required/babel/base/babel.ins
@@ -26,7 +26,7 @@
%% and covered by LPPL is defined by the unpacking scripts (with
%% extension .ins) which are part of the distribution.
%%
-\def\filedate{2021/05/16}
+\def\filedate{2021/06/02}
\def\batchfile{babel.ins}
\input docstrip.tex
diff --git a/macros/latex/required/babel/base/babel.pdf b/macros/latex/required/babel/base/babel.pdf
index f8be6b4104..a184df48a2 100644
--- a/macros/latex/required/babel/base/babel.pdf
+++ b/macros/latex/required/babel/base/babel.pdf
Binary files differ
diff --git a/macros/latex/required/babel/base/bbcompat.dtx b/macros/latex/required/babel/base/bbcompat.dtx
index 4310c06af1..601f524b83 100644
--- a/macros/latex/required/babel/base/bbcompat.dtx
+++ b/macros/latex/required/babel/base/bbcompat.dtx
@@ -30,7 +30,7 @@
%
% \iffalse
%<*dtx>
-\ProvidesFile{bbcompat.dtx}[2021/05/16 v3.59]
+\ProvidesFile{bbcompat.dtx}[2021/06/02 v3.60]
%</dtx>
%
%% File 'bbcompat.dtx'
diff --git a/macros/luatex/latex/lua-typo/README.md b/macros/luatex/latex/lua-typo/README.md
index 5e88fafb62..64373e6913 100644
--- a/macros/luatex/latex/lua-typo/README.md
+++ b/macros/luatex/latex/lua-typo/README.md
@@ -48,12 +48,26 @@ Recommended loactions for installation:
Changes
-------
-- First release version: 0.30, March 2021.
+* First release version: 0.30, March 2021.
-- v.0.32: bug fixes
- better protection against nil nodes,
- new page detection corrected,
- homeoarchy detection improved.
+* v.0.32: bug fixes
+ - better protection against nil nodes,
+ - new page detection corrected,
+ - homeoarchy detection improved.
+
+* v.0.40 (not released): bug fixes
+ - in some cases parlines count was wrong, fixed;
+ - partial improvement of short pages detection.
+
+* v.0.50: new implementation, May 2021
+ - callback change: `pre_shipout_filter` instead of `pre_output_filter`
+ this change requires latex release 2021-06-01;
+ - rollback enabled in case `pre_shipout_filter` is missing;
+ - footnotes are scanned now;
+ - overfull box detection fixed (works for equations and tt fonts now);
+ - short pages detection fixed;
+ - coloration of faulty lines improved;
+ - all flaws found are now recorded into file "`\jobname`.typo".
--
Copyright 2020--2021 Daniel Flipo
diff --git a/macros/luatex/latex/lua-typo/doc/lua-typo-demo.pdf b/macros/luatex/latex/lua-typo/doc/lua-typo-demo.pdf
index f5c82e7015..89951bdf86 100644
--- a/macros/luatex/latex/lua-typo/doc/lua-typo-demo.pdf
+++ b/macros/luatex/latex/lua-typo/doc/lua-typo-demo.pdf
Binary files differ
diff --git a/macros/luatex/latex/lua-typo/doc/lua-typo-fr.pdf b/macros/luatex/latex/lua-typo/doc/lua-typo-fr.pdf
index 5b2c788bd0..315c8306e1 100644
--- a/macros/luatex/latex/lua-typo/doc/lua-typo-fr.pdf
+++ b/macros/luatex/latex/lua-typo/doc/lua-typo-fr.pdf
Binary files differ
diff --git a/macros/luatex/latex/lua-typo/doc/lua-typo.pdf b/macros/luatex/latex/lua-typo/doc/lua-typo.pdf
index 764e6374a4..5b2b2dcbdf 100644
--- a/macros/luatex/latex/lua-typo/doc/lua-typo.pdf
+++ b/macros/luatex/latex/lua-typo/doc/lua-typo.pdf
Binary files differ
diff --git a/macros/luatex/latex/lua-typo/lua-typo-2021-04-18.sty b/macros/luatex/latex/lua-typo/lua-typo-2021-04-18.sty
new file mode 100644
index 0000000000..1580bcb147
--- /dev/null
+++ b/macros/luatex/latex/lua-typo/lua-typo-2021-04-18.sty
@@ -0,0 +1,843 @@
+%%
+%% This is file `lua-typo.sty' v0.40,
+%% generated with the docstrip utility.
+%%
+%% The original source files were:
+%%
+%% lua-typo.dtx (with options: `sty')
+%%
+%% IMPORTANT NOTICE:
+%% For the copyright see the source file `lua-typo.dtx'.
+%%
+\NeedsTeXFormat{LaTeX2e}[2020/01/01]
+\ProvidesPackage{lua-typo}
+ [2021/04/18 v.0.40 Daniel Flipo]
+\ifdefined\directlua
+ \RequirePackage{luatexbase,luacode,luacolor}
+ \RequirePackage{kvoptions,atveryend}
+\else
+ \PackageError{This package is meant for LuaTeX only! Aborting}
+ {No more information available, sorry!}
+\fi
+\newdimen\luatypoLLminWD
+\newdimen\luatypoBackPI
+\newdimen\luatypoBackFuzz
+\newcount\luatypoStretchMax
+\newcount\luatypoHyphMax
+\newcount\luatypoPageMin
+\newcount\luatypoMinFull
+\newcount\luatypoMinPart
+\newcount\luatypo@LANGno
+\newcount\luatypo@options
+\newtoks\luatypo@single
+\newtoks\luatypo@double
+
+\begin{luacode}
+luatypo = { }
+\end{luacode}
+\SetupKeyvalOptions{
+ family=luatypo,
+ prefix=LT@,
+}
+\DeclareBoolOption[false]{ShowOptions}
+\DeclareBoolOption[false]{None}
+\DeclareBoolOption[false]{All}
+\DeclareBoolOption[false]{BackParindent}
+\DeclareBoolOption[false]{ShortLines}
+\DeclareBoolOption[false]{ShortPages}
+\DeclareBoolOption[false]{OverfullLines}
+\DeclareBoolOption[false]{UnderfullLines}
+\DeclareBoolOption[false]{Widows}
+\DeclareBoolOption[false]{Orphans}
+\DeclareBoolOption[false]{EOPHyphens}
+\DeclareBoolOption[false]{RepeatedHyphens}
+\DeclareBoolOption[false]{ParLastHyphen}
+\DeclareBoolOption[false]{EOLShortWords}
+\DeclareBoolOption[false]{FirstWordMatch}
+\DeclareBoolOption[false]{LastWordMatch}
+\AddToKeyvalOption{luatypo}{All}{%
+ \LT@ShortLinestrue \LT@ShortPagestrue
+ \LT@OverfullLinestrue \LT@UnderfullLinestrue
+ \LT@Widowstrue \LT@Orphanstrue
+ \LT@EOPHyphenstrue \LT@RepeatedHyphenstrue
+ \LT@ParLastHyphentrue \LT@EOLShortWordstrue
+ \LT@FirstWordMatchtrue \LT@LastWordMatchtrue
+ \LT@BackParindenttrue
+}
+\ProcessKeyvalOptions{luatypo}
+\AtEndOfPackage{%
+ \ifLT@None
+ \directlua{ luatypo.None = true }%
+ \else
+ \directlua{ luatypo.None = false }%
+ \fi
+ \ifLT@BackParindent
+ \advance\luatypo@options by 1
+ \directlua{ luatypo.BackParindent = true }%
+ \else
+ \directlua{ luatypo.BackParindent = false }%
+ \fi
+ \ifLT@ShortLines
+ \advance\luatypo@options by 1
+ \directlua{ luatypo.ShortLines = true }%
+ \else
+ \directlua{ luatypo.ShortLines = false }%
+ \fi
+ \ifLT@ShortPages
+ \advance\luatypo@options by 1
+ \directlua{ luatypo.ShortPages = true }%
+ \else
+ \directlua{ luatypo.ShortPages = false }%
+ \fi
+ \ifLT@OverfullLines
+ \advance\luatypo@options by 1
+ \directlua{ luatypo.OverfullLines = true }%
+ \else
+ \directlua{ luatypo.OverfullLines = false }%
+ \fi
+ \ifLT@UnderfullLines
+ \advance\luatypo@options by 1
+ \directlua{ luatypo.UnderfullLines = true }%
+ \else
+ \directlua{ luatypo.UnderfullLines = false }%
+ \fi
+ \ifLT@Widows
+ \advance\luatypo@options by 1
+ \directlua{ luatypo.Widows = true }%
+ \else
+ \directlua{ luatypo.Widows = false }%
+ \fi
+ \ifLT@Orphans
+ \advance\luatypo@options by 1
+ \directlua{ luatypo.Orphans = true }%
+ \else
+ \directlua{ luatypo.Orphans = false }%
+ \fi
+ \ifLT@EOPHyphens
+ \advance\luatypo@options by 1
+ \directlua{ luatypo.EOPHyphens = true }%
+ \else
+ \directlua{ luatypo.EOPHyphens = false }%
+ \fi
+ \ifLT@RepeatedHyphens
+ \advance\luatypo@options by 1
+ \directlua{ luatypo.RepeatedHyphens = true }%
+ \else
+ \directlua{ luatypo.RepeatedHyphens = false }%
+ \fi
+ \ifLT@ParLastHyphen
+ \advance\luatypo@options by 1
+ \directlua{ luatypo.ParLastHyphen = true }%
+ \else
+ \directlua{ luatypo.ParLastHyphen = false }%
+ \fi
+ \ifLT@EOLShortWords
+ \advance\luatypo@options by 1
+ \directlua{ luatypo.EOLShortWords = true }%
+ \else
+ \directlua{ luatypo.EOLShortWords = false }%
+ \fi
+ \ifLT@FirstWordMatch
+ \advance\luatypo@options by 1
+ \directlua{ luatypo.FirstWordMatch = true }%
+ \else
+ \directlua{ luatypo.FirstWordMatch = false }%
+ \fi
+ \ifLT@LastWordMatch
+ \advance\luatypo@options by 1
+ \directlua{ luatypo.LastWordMatch = true }%
+ \else
+ \directlua{ luatypo.LastWordMatch = false }%
+ \fi
+}
+\ifLT@ShowOptions
+ \GenericWarning{* }{%
+ *** List of possible options for lua-typo ***\MessageBreak
+ [Default values between brackets]%
+ \MessageBreak
+ ShowOptions [false]\MessageBreak
+ None [false]\MessageBreak
+ BackParindent [false]\MessageBreak
+ ShortLines [false]\MessageBreak
+ ShortPages [false]\MessageBreak
+ OverfullLines [false]\MessageBreak
+ UnderfullLines [false]\MessageBreak
+ Widows [false]\MessageBreak
+ Orphans [false]\MessageBreak
+ EOPHyphens [false]\MessageBreak
+ RepeatedHyphens [false]\MessageBreak
+ ParLastHyphen [false]\MessageBreak
+ EOLShortWords [false]\MessageBreak
+ FirstWordMatch [false]\MessageBreak
+ LastWordMatch [false]\MessageBreak
+ \MessageBreak
+ *********************************************%
+ \MessageBreak Lua-typo [ShowOptions]
+ }%
+\fi
+\AtBeginDocument{%
+ \directlua{
+ luatypo.HYPHmax = tex.count.luatypoHyphMax
+ luatypo.PAGEmin = tex.count.luatypoPageMin
+ luatypo.Stretch = tex.count.luatypoStretchMax
+ luatypo.MinFull = tex.count.luatypoMinFull
+ luatypo.MinPart = tex.count.luatypoMinPart
+ luatypo.LLminWD = tex.dimen.luatypoLLminWD
+ luatypo.BackPI = tex.dimen.luatypoBackPI
+ luatypo.BackFuzz = tex.dimen.luatypoBackFuzz
+ }%
+}
+\AtVeryEndDocument{%
+\ifnum\luatypo@options = 0 \LT@Nonetrue \fi
+\ifLT@None
+ \directlua{
+ texio.write_nl(' ')
+ texio.write_nl('***********************************')
+ texio.write_nl('*** lua-typo loaded with NO option:')
+ texio.write_nl('*** NO CHECK PERFORMED! ***')
+ texio.write_nl('***********************************')
+ texio.write_nl(' ')
+ }%
+\else
+ \directlua{
+ texio.write_nl(' ')
+ texio.write_nl('*************************************')
+ if luatypo.pagelist == "" then
+ texio.write_nl('*** lua-typo: No Typo Flaws found.')
+ else
+ texio.write_nl('*** lua-typo: WARNING *************')
+ texio.write_nl('The following pages need attention: ')
+ texio.write(luatypo.pagelist)
+ end
+ texio.write_nl('***********************************')
+ texio.write_nl(' ')
+ }%
+\fi}
+\newcommand*{\luatypoOneChar}[2]{%
+ \def\luatypo@LANG{#1}\luatypo@single={#2}%
+ \ifcsname l@\luatypo@LANG\endcsname
+ \luatypo@LANGno=\the\csname l@\luatypo@LANG\endcsname \relax
+ \directlua{
+ local langno = \the\luatypo@LANGno
+ local string = \the\luatypo@single
+ luatypo.single[langno] = " "
+ for p, c in utf8.codes(string) do
+ local s = string.char(c)
+ luatypo.single[langno] = luatypo.single[langno] .. s
+ end
+ }%
+ \else
+ \PackageWarning{luatypo}{Unknown language "\luatypo@LANG",
+ \MessageBreak \protect\luatypoOneChar\space command ignored}%
+ \fi}
+\newcommand*{\luatypoTwoChars}[2]{%
+ \def\luatypo@LANG{#1}\luatypo@double={#2}%
+ \ifcsname l@\luatypo@LANG\endcsname
+ \luatypo@LANGno=\the\csname l@\luatypo@LANG\endcsname \relax
+ \directlua{
+ local langno = \the\luatypo@LANGno
+ local string = \the\luatypo@double
+ luatypo.double[langno] = " "
+ for p, c in utf8.codes(string) do
+ local s = string.char(c)
+ luatypo.double[langno] = luatypo.double[langno] .. s
+ end
+ }%
+ \else
+ \PackageWarning{luatypo}{Unknown language "\luatypo@LANG",
+ \MessageBreak \protect\luatypoTwoChars\space command ignored}%
+ \fi}
+\newcommand*{\luatypoSetColor}[2]{%
+ \begingroup
+ \color{#2}%
+ \directlua{luatypo.colortbl[#1]=\the\LuaCol@Attribute}%
+ \endgroup
+}
+\begin{luacode}
+luatypo.single = { }
+luatypo.double = { }
+luatypo.colortbl = { }
+luatypo.pagelist = ""
+
+local char_to_discard = { }
+char_to_discard[string.byte(",")] = true
+char_to_discard[string.byte(".")] = true
+char_to_discard[string.byte("!")] = true
+char_to_discard[string.byte("?")] = true
+char_to_discard[string.byte(":")] = true
+char_to_discard[string.byte(";")] = true
+char_to_discard[string.byte("-")] = true
+
+local split_lig = { }
+split_lig[0xFB00] = "ff"
+split_lig[0xFB01] = "fi"
+split_lig[0xFB02] = "fl"
+split_lig[0xFB03] = "ffi"
+split_lig[0xFB04] = "ffl"
+split_lig[0xFB05] = "st"
+split_lig[0xFB06] = "st"
+
+local DISC = node.id("disc")
+local GLYPH = node.id("glyph")
+local GLUE = node.id("glue")
+local KERN = node.id("kern")
+local HLIST = node.id("hlist")
+local LPAR = node.id("local_par")
+local MKERN = node.id("margin_kern")
+local PENALTY = node.id("penalty")
+local USRSKIP = 0
+local PARSKIP = 3
+local LFTSKIP = 8
+local RGTSKIP = 9
+local TOPSKIP = 10
+local PARFILL = 15
+local LINE = 1
+local BOX = 2
+local EQN = 6
+local USER = 0
+local HYPH = 0x2D
+local LIGA = 0x102
+local parline = 0
+
+local effective_glue = node.effective_glue
+local set_attribute = node.set_attribute
+local slide = node.slide
+local traverse = node.traverse
+local traverse_id = node.traverse_id
+local has_field = node.has_field
+local uses_font = node.uses_font
+local is_glyph = node.is_glyph
+
+local color_node = function (node, color)
+ local attr = oberdiek.luacolor.getattribute()
+ if node and node.id == DISC then
+ local pre = node.pre
+ local post = node.post
+ local repl = node.replace
+ if pre then
+ set_attribute(pre,attr,color)
+ end
+ if post then
+ set_attribute(post,attr,color)
+ end
+ if repl then
+ set_attribute(repl,attr,color)
+ end
+ elseif node then
+ set_attribute(node,attr,color)
+ end
+end
+local color_hlist = function (head, color)
+ local first = head.head
+ for n in traverse(first) do
+ color_node(n, color)
+ end
+end
+local color_line = function (head, color)
+ local first = head.head
+ for n in traverse(first) do
+ if n and n.id == HLIST then
+ color_hlist(n, color)
+ else
+ color_node(n, color)
+ end
+ end
+end
+local signature = function (node, string, swap)
+ local n = node
+ local str = string
+ if n and n.id == GLYPH then
+ local b, id = is_glyph(n)
+ if b and not char_to_discard[b] then
+ if b == 0x2019 then b = 0x27 end
+ if b < 0x100 then
+ str = str .. string.char(b)
+ elseif split_lig[b] then
+ local c = split_lig[b]
+ if swap then
+ c = string.reverse(c)
+ end
+ str = str .. c
+ elseif n.subtype == LIGA and b > 0xE000 then
+ local c = string.sub(b,-2)
+ if swap then
+ c = string.reverse(c)
+ end
+ str = str .. c
+ end
+ end
+ elseif n and n.id == DISC then
+ local pre = n.pre
+ local post = n.post
+ local c1 = ""
+ local c2 = ""
+ if pre and pre.char and pre.char ~= HYPH and pre.char < 0x100 then
+ c1 = string.char(pre.char)
+ end
+ if post and post.char then
+ if post.char < 0x100 then
+ c2 = string.char(post.char)
+ elseif split_lig[post.char] then
+ c2 = split_lig[post.char]
+ if swap then
+ c2 = string.reverse(c2)
+ end
+ end
+ end
+ if swap then
+ str = str .. c2 .. c1
+ else
+ str = str .. c1 .. c2
+ end
+ end
+ local len = string.len(str)
+ if string.find(str, "_") then
+ len = len - 1
+ end
+ return len, str
+end
+local check_last_word = function (old, node, flag)
+ local COLOR = luatypo.colortbl[11]
+ local match = false
+ local new = ""
+ local maxlen = 0
+ if flag and node then
+ local swap = true
+ local lastn = node
+ while lastn and lastn.id ~= GLYPH and lastn.id ~= DISC do
+ lastn = lastn.prev
+ end
+ local n = lastn
+ while n and n.id ~= GLUE do
+ maxlen, new = signature (n, new, swap)
+ n = n.prev
+ end
+ if n and n.id == GLUE then
+ new = new .. "_"
+ repeat
+ n = n.prev
+ maxlen, new = signature (n, new, swap)
+ until not n or n.id == GLUE
+ end
+ new = string.reverse(new)
+ local MinFull = luatypo.MinFull
+ local MinPart = luatypo.MinPart
+ MinFull = math.min(MinPart,MinFull)
+ local k = MinPart
+ local oldlast = string.gsub (old, '.*_', '')
+ local newlast = string.gsub (new, '.*_', '')
+ local i = string.find(new, "_")
+ if i and i > maxlen - MinPart + 1 then
+ k = MinPart + 1
+ end
+ local oldsub = string.sub(old,-k)
+ local newsub = string.sub(new,-k)
+ local l = string.len(new)
+ if oldsub == newsub and l >= k then
+ match = true
+ elseif oldlast == newlast and string.len(newlast) >= MinFull then
+ match = true
+ oldsub = oldlast
+ newsub = newlast
+ k = string.len(newlast)
+ end
+ if match then
+ local osub = oldsub
+ local nsub = newsub
+ while osub == nsub and k <= maxlen do
+ k = k +1
+ osub = string.sub(old,-k)
+ nsub = string.sub(new,-k)
+ if osub == nsub then
+ newsub = nsub
+ end
+ end
+ pageflag = true
+ newsub = string.gsub(newsub, '^_', '')
+ oldsub = string.reverse(newsub)
+ local newsub = ""
+ local n = lastn
+ repeat
+ if n and n.id ~= GLUE then
+ color_node(n, COLOR)
+ l, newsub = signature(n, newsub, swap)
+ elseif n then
+ newsub = newsub .. "_"
+ end
+ n = n.prev
+ until not n or newsub == oldsub or l >= k
+ end
+ end
+ return new
+end
+local check_first_word = function (old, node, flag)
+ local COLOR = luatypo.colortbl[10]
+ local match = false
+ local swap = false
+ local new = ""
+ local maxlen = 0
+ local start = node
+ local n = start
+ while n and n.id ~= GLYPH and n.id ~= DISC do
+ n = n.next
+ end
+ while n and n.id ~= GLUE do
+ maxlen, new = signature (n, new, swap)
+ n = n.next
+ end
+ if n and n.id == GLUE then
+ new = new .. "_"
+ repeat
+ n = n.next
+ maxlen, new = signature (n, new, swap)
+ until not n or n.id == GLUE
+ end
+ if flag then
+ local MinFull = luatypo.MinFull
+ local MinPart = luatypo.MinPart
+ MinFull = math.min(MinPart,MinFull)
+ local k = MinPart
+ local oldsub = ""
+ local newsub = ""
+ local oldfirst = string.gsub (old, '_.*', '')
+ local newfirst = string.gsub (new, '_.*', '')
+ local i = string.find(new, "_")
+ if i and i <= MinPart then
+ k = MinPart + 1
+ end
+ local oldsub = string.sub(old,1,k)
+ local newsub = string.sub(new,1,k)
+ local l = string.len(newsub)
+ if oldsub == newsub and l >= k then
+ match = true
+ elseif oldfirst == newfirst and string.len(newfirst) >= MinFull then
+ match = true
+ oldsub = oldfirst
+ newsub = newfirst
+ k = string.len(newfirst)
+ end
+ if match then
+ local osub = oldsub
+ local nsub = newsub
+ while osub == nsub and k <= maxlen do
+ k = k + 1
+ osub = string.sub(old,1,k)
+ nsub = string.sub(new,1,k)
+ if osub == nsub then
+ newsub = nsub
+ end
+ end
+ pageflag = true
+ newsub = string.gsub(newsub, '_$', '') --$
+ oldsub = newsub
+ local newsub = ""
+ local k = string.len(oldsub)
+ local n = start
+ repeat
+ if n and n.id ~= GLUE then
+ color_node(n, COLOR)
+ l, newsub = signature(n, newsub, swap)
+ elseif n then
+ newsub = newsub .. "_"
+ end
+ n = n.next
+ until not n or newsub == oldsub or l >= k
+ end
+ end
+ return new
+end
+local check_regexpr = function (glyph)
+ local COLOR = luatypo.colortbl[3]
+ local lang = glyph.lang
+ local match = false
+ local lchar, id = is_glyph(glyph)
+ local previous = glyph.prev
+ if lang and luatypo.single[lang] then
+ if lchar and lchar < 0x100 and previous and previous.id == GLUE then
+ match = string.find(luatypo.single[lang], string.char(lchar))
+ if match then
+ pageflag = true
+ color_node(glyph,COLOR)
+ end
+ end
+ end
+ if lang and luatypo.double[lang] then
+ if lchar and previous and previous.id == GLYPH then
+ local pchar, id = is_glyph(previous)
+ local pprev = previous.prev
+ if pchar and pchar < 0x100 and pprev and pprev.id == GLUE then
+ local pattern = string.char(pchar) .. string.char(lchar)
+ match = string.find(luatypo.double[lang], pattern)
+ if match then
+ pageflag = true
+ color_node(previous,COLOR)
+ color_node(glyph,COLOR)
+ end
+ end
+ elseif lchar and previous and previous.id == KERN then
+ local pprev = previous.prev
+ if pprev and pprev.id == GLYPH then
+ local pchar, id = is_glyph(pprev)
+ local ppprev = pprev.prev
+ if pchar and pchar < 0x100 and
+ ppprev and ppprev.id == GLUE then
+ local pattern = string.char(pchar) .. string.char(lchar)
+ match = string.find(luatypo.double[lang], pattern)
+ if match then
+ pageflag = true
+ color_node(pprev,COLOR)
+ color_node(glyph,COLOR)
+ end
+ end
+ end
+ end
+ end
+end
+local show_pre_disc = function (disc, color)
+ local n = disc
+ while n and n.id ~= GLUE do
+ color_node(n, color)
+ n = n.prev
+ end
+ return n
+end
+luatypo.check_page = function (head)
+ local PAGEmin = luatypo.PAGEmin
+ local HYPHmax = luatypo.HYPHmax
+ local LLminWD = luatypo.LLminWD
+ local BackPI = luatypo.BackPI
+ local BackFuzz = luatypo.BackFuzz
+ local BackParindent = luatypo.BackParindent
+ local ShortLines = luatypo.ShortLines
+ local ShortPages = luatypo.ShortPages
+ local OverfullLines = luatypo.OverfullLines
+ local UnderfullLines = luatypo.UnderfullLines
+ local Widows = luatypo.Widows
+ local Orphans = luatypo.Orphans
+ local EOPHyphens = luatypo.EOPHyphens
+ local RepeatedHyphens = luatypo.RepeatedHyphens
+ local FirstWordMatch = luatypo.FirstWordMatch
+ local ParLastHyphen = luatypo.ParLastHyphen
+ local EOLShortWords = luatypo.EOLShortWords
+ local LastWordMatch = luatypo.LastWordMatch
+ local pageno = tex.getcount("c@page")
+ local Stretch = math.max(luatypo.Stretch/100,1)
+ local blskip = tex.getglue("baselineskip")
+ local textht = tex.getdimen("textheight")
+ local vpos_min = (PAGEmin+1) * blskip
+ local vpos = 0
+
+ local pageflag = false
+ local orphanflag = false
+ local widowflag = false
+ local lwhyphflag = false
+ local pageshort = false
+ local firstwd = ""
+ local lastwd = ""
+ local hyphcount = 0
+ local pageline = 0
+
+ while head do
+ local nextnode = head.next
+ local prevnode = head.prev
+ local pprevnode = nil
+ if prevnode then
+ pprevnode = prevnode.prev
+ end
+ if head.id == HLIST and head.subtype == LINE then
+ vpos = vpos + head.height + head.depth
+ local first = head.head
+ while first.id == MKERN or
+ (first.id == GLUE and first.subtype == LFTSKIP) do
+ first = first.next
+ end
+ local ListItem = false
+ pageline = pageline + 1
+ if head.glue_set == 1 and head.glue_sign == 2 and
+ head.glue_order == 0 and OverfullLines then
+ pageflag = true
+ local COLOR = luatypo.colortbl[7]
+ color_line (head, COLOR)
+ elseif head.glue_set >= Stretch and head.glue_sign == 1 and
+ head.glue_order == 0 and UnderfullLines then
+ local COLOR = luatypo.colortbl[8]
+ pageflag = true
+ color_line (head, COLOR)
+ end
+ if first.id == LPAR then
+ hyphcount = 0
+ parline = 1
+ if not nextnode then
+ orphanflag = true
+ end
+ local nn = first.next
+ if nn and nn.id == HLIST and nn.subtype == BOX then
+ ListItem = true
+ end
+ else
+ parline = parline + 1
+ end
+ if FirstWordMatch then
+ local flag = not ListItem
+ firstwd = check_first_word(firstwd, first, flag)
+ end
+ local ln = slide(first)
+ local pn = ln.prev
+ if pn and pn.id == GLUE and pn.subtype == PARFILL then
+ hyphcount = 0
+ orphanflag = false
+ if pageline == 1 and parline > 1 then
+ widowflag = true
+ end
+ local PFskip = effective_glue(pn,head)
+ if ShortLines then
+ local llwd = tex.hsize - PFskip
+ if llwd < LLminWD then
+ pageflag = true
+ local COLOR = luatypo.colortbl[6]
+ local attr = oberdiek.luacolor.getattribute()
+ color_line (head, COLOR)
+ end
+ end
+ if BackParindent and PFskip < BackPI and PFskip > BackFuzz then
+ pageflag = true
+ local COLOR = luatypo.colortbl[12]
+ local attr = oberdiek.luacolor.getattribute()
+ color_line (head, COLOR)
+ end
+ if LastWordMatch then
+ local flag = textline
+ if PFskip > BackPI then
+ flag = false
+ end
+ lastwd = check_last_word(lastwd, pn, flag)
+ end
+ elseif pn and pn.id == DISC then
+ hyphcount = hyphcount + 1
+ if LastWordMatch then
+ lastwd = check_last_word(lastwd, ln, true)
+ end
+ if hyphcount > HYPHmax and RepeatedHyphens then
+ local COLOR = luatypo.colortbl[2]
+ local pg = show_pre_disc (pn,COLOR)
+ pageflag = true
+ end
+ if not nextnode and EOPHyphens then
+ lwhyphflag = true
+ end
+ if nextnode and ParLastHyphen then
+ local nnnode = nextnode.next
+ local nnnnode = nil
+ if nnnode and nnnode.next then
+ nnnnode = nnnode.next
+ if nnnnode and nnnnode.id == HLIST and
+ nnnnode.subtype == 1 and nnnnode.glue_order == 2 then
+ local COLOR = luatypo.colortbl[0]
+ local pg = show_pre_disc (pn,COLOR)
+ pageflag = true
+ end
+ end
+ end
+ else
+ hyphcount = 0
+ if LastWordMatch and pn then
+ lastwd = check_last_word(lastwd, pn, true)
+ end
+ if EOLShortWords then
+ while pn and pn.id ~= GLYPH and pn.id ~= HLIST do
+ pn = pn.prev
+ end
+ if pn and pn.id == GLYPH then
+ check_regexpr(pn,line)
+ end
+ end
+ end
+ if widowflag and Widows then
+ pageflag = true
+ widowflag = false
+ local COLOR = luatypo.colortbl[4]
+ color_line (head, COLOR)
+ end
+ if orphanflag and Orphans then
+ pageflag = true
+ local COLOR = luatypo.colortbl[5]
+ color_line (head, COLOR)
+ end
+ if lwhyphflag and EOPHyphens then
+ pageflag = true
+ local COLOR = luatypo.colortbl[1]
+ local pg = show_pre_disc (pn,COLOR)
+ end
+ elseif head.id == GLUE and head.subtype == USRSKIP then
+ vpos = vpos + head.width
+ if not nextnode and ShortPages and
+ pageline > 1 and pageline < PAGEmin then
+ pageshort = true
+ end
+ if pageshort and vpos < vpos_min then
+ pageflag = true
+ local COLOR = luatypo.colortbl[9]
+ local n = head
+ repeat
+ n = n.prev
+ until n.id == HLIST and (n.subtype == LINE or n.subtype == EQN)
+ if n then
+ color_line(n, COLOR)
+ end
+ end
+ elseif head.id == GLUE then
+ vpos = vpos + head.width
+ end
+ head = nextnode
+ end
+ if pageflag then
+ local pl = luatypo.pagelist
+ local p = tonumber(string.match(pl, "%s(%d+),%s$"))
+ if not p or pageno > p then
+ luatypo.pagelist = luatypo.pagelist .. tostring(pageno) .. ", "
+ end
+ end
+ return true
+end
+return luatypo.check_page
+\end{luacode}
+\AtEndOfPackage{%
+ \directlua{
+ if not luatypo.None then
+ luatexbase.add_to_callback
+ ("pre_output_filter",luatypo.check_page,"check_page")
+ end
+ }
+}
+
+\InputIfFileExists{lua-typo.cfg}%
+ {\PackageInfo{lua-typo.sty}{'lua-typo.cfg' file loaded}}%
+ {\PackageInfo{lua-typo.sty}{'lua-typo.cfg' file not found.
+ \MessageBreak Providing default values.}%
+ \definecolor{mygrey}{gray}{0.6}%
+ \definecolor{myred}{rgb}{1,0.55,0}
+ \luatypoSetColor0{red}% Paragraph last full line hyphenated
+ \luatypoSetColor1{red}% Page last word hyphenated
+ \luatypoSetColor2{red}% Hyphens ending two many consecutive lines
+ \luatypoSetColor3{red}% Short word at end of line
+ \luatypoSetColor4{cyan}% Widow
+ \luatypoSetColor5{cyan}% Orphan
+ \luatypoSetColor6{cyan}% Paragraph ending on a short line
+ \luatypoSetColor7{blue}% Overfull lines
+ \luatypoSetColor8{blue}% Underfull lines
+ \luatypoSetColor9{red}% Nearly empty page
+ \luatypoSetColor{10}{myred}% First word matches
+ \luatypoSetColor{11}{myred}% Last word matches
+ \luatypoSetColor{12}{mygrey}% Paragraph ending on a nearly full line
+ \luatypoBackPI=1em\relax
+ \luatypoBackFuzz=2pt\relax
+ \luatypoLLminWD=2\parindent\relax
+ \luatypoStretchMax=200\relax
+ \luatypoHyphMax=2\relax
+ \luatypoPageMin=5\relax
+ \luatypoMinFull=4\relax
+ \luatypoMinPART=4\relax
+ }%
+%%
+%%
+%% End of file `lua-typo.sty'.
diff --git a/macros/luatex/latex/lua-typo/source/lua-typo.dtx b/macros/luatex/latex/lua-typo/source/lua-typo.dtx
index f783033fdd..ebcd727a67 100644
--- a/macros/luatex/latex/lua-typo/source/lua-typo.dtx
+++ b/macros/luatex/latex/lua-typo/source/lua-typo.dtx
@@ -1,4 +1,4 @@
-% \CheckSum{336}
+% \CheckSum{373}
%
% \iffalse meta-comment
%
@@ -34,8 +34,9 @@
\postamble
\endpostamble
\let\MetaPrefix\DoubleperCent
+\askforoverwritefalse
\generate{%
- \file{lua-typo.sty}{\from{lua-typo.dtx}{sty}}% {sty,msg,dbg}
+ \file{lua-typo.sty}{\from{lua-typo.dtx}{sty}}% {sty,dbg}
\nopreamble
\file{lua-typo.cfg}{\from{lua-typo.dtx}{cfg}}%
\file{lua-typo-fr.ltx}{\from{lua-typo.dtx}{driver,docfr}}%
@@ -49,6 +50,9 @@
\csname fi\endcsname
%</gobble>
%<*driver>
+\RequirePackage{pdfmanagement-testphase}
+%<-docfr>\DeclareDocumentMetadata{pdfstandard=A-2b, lang=en-GB}
+%<-doc>\DeclareDocumentMetadata{pdfstandard=A-2b, lang=fr-FR}
\documentclass[a4paper]{ltxdoc}
\usepackage[dvipsnames]{xcolor}
\usepackage{fontspec}
@@ -79,14 +83,15 @@
%<-doc>\let\FrenchDoc\begingroup\let\endFrenchDoc\endgroup
%
%<-docfr>\let\FrenchDoc\comment\let\endFrenchDoc\endcomment
-%<-docfr|doc>\RecordChanges
-%<-docfr|doc>\AtEndDocument{%
-%<-docfr|doc> \clearpage
-%<-docfr|doc> \section{Change History}%
-%<-docfr|doc> \GlossaryPrologue{}%
-%<-docfr|doc> Changes are listed in reverse order (latest first) from version~0.1
-%<-docfr|doc> \PrintChanges
-%<-docfr|doc>}
+%<-docfr>\RecordChanges
+%<-docfr>\AtEndDocument{%
+%<-docfr> \clearpage
+%<-docfr> \section{Change History}%
+%<-docfr> \GlossaryPrologue{}%
+%<-docfr> Changes are listed in reverse order (latest first)
+%<-docfr> from version~0.30.
+%<-docfr> \PrintChanges
+%<-docfr>}
%
\newcommand*\file[1]{\texttt{#1}}
\newcommand*\pkg[1]{\texttt{#1}}
@@ -124,8 +129,8 @@
% L’extension \pkg{lua-typo} décrite ci-dessous%
% \footnote{Version \fileversion, mise à jour le \filedate.}
% permet de mettre en lumière par un changement de couleur, les
-% lignes typographiquement imparfaites d'un fichier PDF produit par
-% LuaLaTex. Une liste des pages concernées est affichée à la fin du
+% lignes typographiquement imparfaites d’un fichier PDF produit par
+% LuaLaTeX. Une liste des pages concernées est affichée à la fin du
% fichier \file{.log}, permettant un accès rapide aux pages
% potentiellement défectueuses.
%
@@ -137,7 +142,7 @@
% lignes consécutives, des dernières lignes d’alinéa trop courtes ou
% presque pleines, des pages quasi vides.
% La répétition d’un même mot ou partie de mot au début ou à la fin
-% de deux lignes consécutives est détectée.
+% de deux lignes consécutives est aussi détectée.
% La présence en fin de ligne de certains mots très courts (une ou
% deux lettres, liste dépendant de la langue) peut également être
% recherchée.
@@ -145,9 +150,9 @@
% \pkg{lua-typo}, ne fonctionne qu’avec LuaLaTeX et
% \emph{ne corrige aucun des défauts relevés} mais se contente de
% \emph{signaler} au relecteur les points qui peuvent nécessiter une
-% correction. Seul un œil humain entrainé peut décider si une ligne
+% correction. Seul un humain entrainé peut décider si une ligne
% légèrement lavée est acceptable ou non, ou si la suppression
-% d’une césure malvenue ne va pas provoquer des désordres plus
+% d’une coupure malvenue ne va pas provoquer des désordres plus
% graves encore.
%
% Je conseille de n’appliquer \pkg{lua-typo} que sur des textes
@@ -158,6 +163,13 @@
% \pkg{lua-typo}, il suffit d’ajouter dans le préambule la ligne\\
% |\usepackage[All]{lua-typo}|
%
+% La version courante (0.50) nécessite un noyau LaTeX très récent,
+% 2021/06/01 ou ultérieur. Ceux qui ne disposent que d’un noyau plus
+% ancient reçoivent un message d’avertissement et un message d’erreur
+% «\texttt{Unable to register callback}» ; une version «rollback »
+% est prévue à leur intention, elle se charge par la commande
+% |\usepackage[All]{lua-typo}[=v0.4]|.
+%
% Les fichiers \file{demo.tex} et \file{demo.pdf} fournissent un
% exemple du traitement opéré par \pkg{lua-typo}.
%
@@ -182,7 +194,7 @@
% Le tableau suivant donne le nom des options et le type des
% vérifications proposées :\\[12pt]
% \begin{tabular}{>{\ttfamily}ll}
-% \multicolumn{1}{l}{Nom} & Imperfection à signaler\\ \hline
+% \multicolumn{1}{l}{Nom} & Imperfection à signaler\\ \hline
% All & Active toutes les options ci-dessous\\
% ShortLines & Dernière ligne d’alinéa trop courte ?\\
% BackParindent & Dernière ligne d’alinéa \emph{presque} pleine ?\\
@@ -190,15 +202,16 @@
% OverfullLines & Ligne trop pleine ?\\
% UnderfullLines & Ligne lavée ? \\
% Widows & Veuve (haut de page) ?\\
-% Orphans & Orpheline (bas de page)\\
+% Orphans & Orpheline (bas de page) ?\\
% EOPHyphens & Mot coupé en bas de page ?\\
-% RepeatedHyphens & Césures sur trop de lignes consécutives ?\\
-% ParLastHyphen & Césure à l’avant-dernière ligne d’un alinéa ?\\
+% RepeatedHyphens & Coupures sur trop de lignes consécutives ?\\
+% ParLastHyphen & Coupure à l’avant-dernière ligne d’un alinéa ?\\
% EOLShortWords & Mots courts (1 or 2 lettres) en fin de ligne ?\\
% FirstWordMatch & Même (partie de) mot en début de lignes
% consécutives ?\\
% LastWordMatch & Même (partie de) mot en fin de lignes
% consécutives ?\\
+% FootnoteSplit & Fin de note de bas de page sur page suivante?\\
% \hline
% \end{tabular}\\[12pt]
% Par exemple, pour limiter les vérifications aux lignes trop pleines
@@ -238,28 +251,31 @@
% n’est jamais signalé comme répétition, même s’il est identique
% au mot de la ligne du dessus.
% De même, s’il est identique au dernier mot de la ligne
-% suivante, la répéttion ne sera pas signalée non plus.
+% suivante, la répéttion ne sera pas signalée non plus.
% \item[ShortPages :] lorsque le nombre de lignes d’une page est jugé
% insuffisant (voir ci-dessous), seule la dernière ligne de celle-ci
% est mise en couleur.
% \enlargethispage*{\baselineskip}
% \item[RepeatedHyphens :] de même, lorsque le nombre de lignes
-% consécutives affectées par des césures dépasse le seuil fixé
+% consécutives affectées par des coupures dépasse le seuil fixé
% (voir ci-dessous), ne sont coloriées que les coupures en excès.
% \end{description}
%
-% Enfin, le contenu des notes de bas de pages n’est pas vérifié
-% par \pkg{lua-typo} car celles-ci passent pour l’instant en dessous
-% de nos radars… il faut le savoir.
+% À partir de la version~0.50, le contenu des notes de bas de pages
+% est également vérifié, les notes trop longues pour se terminer sur
+% la page de leur appel sont signalées (option \opt{FootnoteSplit}).
+% Un fichier de suffixe \file{.typo} est créé, il contient la liste
+% complète des défauts repérés avec leur localisation dans le fichier
+% de sortie.
%
% \section{Paramétrage personnalisé}
%
% Pour certaines vérifications faites par \pkg{lua-typo} un
% paramétrage est nécessaire : à partir de quelle limite une dernière
% ligne d’alinéa est-elle considérée comme trop courte ?
-% Combien de césures consécutives en bout de ligne sont-elles
-% acceptables ? Ces réglages dépendent évidemment du contexte, un
-% correcteur de romans aura des exigences plus strictes qu’un auteur
+% Combien de coupures consécutives en bout de ligne sont-elles
+% acceptables ? Ces réglages dépendent évidemment du contexte, un
+% correcteur de romans aura des exigences plus strictes qu’un auteur
% de documentation technique par exemple…
%
% \pkg{lua-typo} permet de modifier le réglage des curseurs soit dans
@@ -278,23 +294,27 @@
%
% Voici la liste complète des paramètres personnalisables avec leur
% valeur par défaut, leurs noms sont systématiquement préfixés par
-% |luatypo| afin d’éviter de possible conflits avec d’autres
+% |luatypo| afin d’éviter de possibles conflits avec d’autres
% extensions.
% \begin{description}
-% \item[BackParindent :] la dernière ligne d’un alinéa fixe
+% \item[BackParindent :] la dernière ligne d’un alinéa fixe
% devrait, soit être pleine ---~en fait se terminer à moins de
% |\luatypoBackFuzz=2pt| de la marge droite~---, soit
% s’en éloigner d’au moins de |\luatypoBackPI=1em|.
%
-% \item[ShortLines :] |\luatypoLLminWD=2\parindent| fixe la
-% longueur minimale acceptable pour la dernière ligne d’un alinéa.
+% \item[ShortLines :] |\luatypoLLminWD=2\parindent|%
+% \footnote{Ou \texttt{20pt} si \cs{parindent=0pt}.}
+% fixe la longueur minimale acceptable pour la dernière ligne
+% d’un alinéa.
%
% \item[ShortPages :] |\luatypoPageMin=5| fixe le nombre minimal de
-% lignes d’une page pour que celle-ci ne pas être
-% signalée comme trop courte.
+% lignes d’une page pour que celle-ci ne soit pas déclarée trop
+% courte. En fait, la position de la dernière ligne est prise en
+% compte afin que les pages de titre ou celles contenant une
+% image ne soient pas signalées comme fautives.
%
% \item[RepeatedHyphens :] |\luatypoHyphMax=2| fixe le nombre maximal
-% acceptable de lignes consécutives terminées par une césure.
+% acceptable de lignes consécutives terminées par un mot coupé.
%
% \item[UnderfullLines :] |\luatypoStretchMax=200| fixe le
% pourcentage maximal acceptable pour l’étirement des
@@ -307,7 +327,7 @@
% réglages par défaut (|\tolerance=200|, |\hbadness=1000|),
% considère comme \emph{Underfull hbox}.
%
-% \item[First/LastWordMatch:] |\luatypoMinFull=3|\hfil et\hfil
+% \item[First/LastWordMatch:] |\luatypoMinFull=3|\hfil et\hfil
% |\luatypoMinPart=4|\hfil nombres\linebreak[4] minimaux de lettres
% identiques (resp. pour un mot complet ou pour une partie de
% mot) au début ou à la fin de deux lignes consécutives
@@ -321,11 +341,11 @@
% ligne de mots très courts (une ou deux lettres)
% qui sont répertoriés dans une des listes
% suivantes (elles dépendent de la langue courante) :\\
-% |\luatypoOneChar{|\meta{language}|}{'|\meta{list of words}|'}|\\
-% |\luatypoTwoChars{|\meta{language}|}{'|\meta{list of words}|'}|
+% |\luatypoOneChar{|\meta{langue}|}{'|\meta{liste de mots}|'}|\\
+% |\luatypoTwoChars{|\meta{langue}|}{'|\meta{liste de mots}|'}|
%
% Lorsque les listes correspondant à la langue du document sont
-% vides, aucune vérification n’est effectuée. Pour l’instant,
+% vides, aucune vérification n’est effectuée. Pour l’instant,
% il y a deux lignes (non actives) prévues pour le français :\\
% |\luatypoOneChar{french}{'À à Ô'}|\\
% |\luatypoTwoChars{french}{'Je Tu Il On'}|
@@ -352,10 +372,10 @@
% \begin{verbatim}
% \definecolor{mygrey}{gray}{0.6}
% \definecolor{myred}{rgb}{1,0.55,0}
-% \luatypoSetColor0{red} % Césure à l’avant-dernière ligne d’un alinéa
-% \luatypoSetColor1{red} % Césure en bas de page
-% \luatypoSetColor2{red} % Césures sur trop de lignes consécutives
-% \luatypoSetColor3{red} % Mot d’une ou deux lettres en fin de ligne
+% \luatypoSetColor0{red} % Coupure à l’avant-dernière ligne
+% \luatypoSetColor1{red} % Coupure en bas de page
+% \luatypoSetColor2{red} % Coupures consécutives
+% \luatypoSetColor3{red} % Mot court en fin de ligne
% \luatypoSetColor4{cyan} % Veuve
% \luatypoSetColor5{cyan} % Orpheline
% \luatypoSetColor6{cyan} % Dernière ligne d’alinéa trop courte
@@ -365,6 +385,7 @@
% \luatypoSetColor{10}{myred} % Répétitions en début de ligne
% \luatypoSetColor{11}{myred} % Répétitions en fin de ligne
% \luatypoSetColor{12}{mygrey}% Dernière ligne d’alinéa presque pleine
+% \luatypoSetColor{13}{cyan} % Note de bas de page éclatée
% \end{verbatim}
% \pkg{lua-typo} charge l’extension graphique \pkg{color}.
% Seules les couleurs portant un nom (\emph{named colors}) peuvent
@@ -412,6 +433,12 @@
% made: if some flaws remain, getting them printed in colour in
% the final document would be a shame!
%
+% This version (0.50) requires the latest LaTeX kernel (dated
+% 2021/06/01). Users running an older kernel will get a warning
+% and an error message ``\texttt{Unable to register callback}’’;
+% for them, a ``rollback’’ version of \pkg{lua-typo} is provided,
+% it can be loaded this way: |\usepackage[All]{lua-typo}[=v0.4]|.
+%
% See files \file{demo.tex} and \file{demo.pdf} for a short example
% (in French).
%
@@ -445,14 +472,15 @@
% OverfullLines & overfull lines?\\
% UnderfullLines & underfull lines?\\
% Widows & widows (top of page)?\\
-% Orphans & orphans (bottom of page)\\
+% Orphans & orphans (bottom of page)?\\
% EOPHyphens & hyphenated word split across two pages?\\
% RepeatedHyphens & too many consecutive hyphens?\\
% ParLastHyphen & paragraph’s last full line hyphenated?\\
% EOLShortWords & short words (1 or 2 chars) at end of line?\\
% FirstWordMatch & same (part of) word starting two consecutive lines?\\
% LastWordMatch & same (part of) word ending two consecutive lines?\\
-% \hline
+% FootnoteSplit & footnotes spread over two pages or more?\\
+% \hline
% \end{tabular}\\[12pt]
% For example, if you want \pkg{lua-typo} to only warn about overfull
% and underfull lines, you can load \pkg{lua-typo} like this:\\
@@ -464,13 +492,13 @@
% taken into account as they are read \emph{i.e.} from left to right.
%
% The list of all available options is printed to the \file{.log}
-% file when option \opt{ShowOptions} is passed to \pkg{lua-typo}, an
-% easy way to get their names without having to look into the
-% documentation.
+% file when option \opt{ShowOptions} is passed to \pkg{lua-typo},
+% this option provides an easy way to get their names without having
+% to look into the documentation.
%
% With option \opt{None}, \pkg{lua-typo} \emph{does absolutely
% nothing}, all checks are disabled as the main function is not added
-% to any LuaTeX callback. It not quite equivalent to commenting out
+% to any LuaTeX callback. It not quite equivalent to commenting out
% the |\usepackage{lua-typo}| line though, as user defined commands
% related to \pkg{lua-typo} are still defined and will not print
% any error message.
@@ -480,22 +508,24 @@
% \item[FirstWordMatch:] the first word of consecutive list items
% is not highlighted, as these repetitions result of the author’s
% choice.
-% \item[LastWordMatch:] a paragraphs’ last word ending ``too far’’
+% \item[LastWordMatch:] a paragraphs’ last word ending ``too far’’
% from the right margin (\emph{i.e.} more than
% |\luatypoBackPI| --default=1em-- away) is never highlighted
% even if it matches the one on the previous line.
% Similarly, if it matches the one on the next line, the latter
% will not be highlighted either.
% \item[ShortPages:] if a page is considered too short, its last
-% line only is highlighted, not the whole page.
+% line only is highlighted, not the whole page.
% \item[RepeatedHyphens:] ditto, when the number of consecutives
% hyphenated lines is too high, only the hyphenated words in
% excess (the last ones) are hightlighted.
% \end{description}
-%
-% Finally, please note that the footnotes’ contents are not checked
-% by \pkg{lua-typo}, I have currently no clue of how to do that,
-% hints are welcome!
+% \enlargethispage*{\baselineskip}
+% Starting with version~0.50, the footnotes’ contents are checked as
+% well by \pkg{lua-typo} and footnotes too long to end on the current
+% page are mentionned as a flaw (option \opt{FootnoteSplit}).
+% The list of all flaws found is written to a specific log-file whose
+% name is suffixed by \file{.typo}.
%
% \section{Customisation}
%
@@ -507,7 +537,7 @@
%
% A default configuration file \file{lua-typo.cfg} is provided
% with all parameters set to their defaults; it is located under
-% the \textsc{texmfdist} directory. It is up to the users to copy
+% the \textsc{texmfdist} directory. It is up to the users to copy
% this file into their working directory (or \textsc{texmfhome} or
% \textsc{texmflocal}) and tune the defaults according to their own
% taste.
@@ -521,17 +551,21 @@
% Here are the parameters names (all prefixed by |luatypo| in order
% to avoid conflicts with other packages) and their default values:
% \begin{description}
-% \item[BackParindent :] paragraphs’ last line should either
+% \item[BackParindent :] paragraphs’ last line should either
% touch the right margin (actually end at less than
-% |\luatypoBackFuzz|, default |2pt|, from it) or leave at least
+% |\luatypoBackFuzz|, default |2pt|, from it) or leave at least
% |\luatypoBackPI|, default |1em|, between its end and the right
% margin.
%
-% \item[ShortLines:] |\luatypoLLminWD=2\parindent| sets the
-% minimum acceptable length for paragraphs’ last lines.
+% \item[ShortLines:] |\luatypoLLminWD=2\parindent|%
+% \footnote{Or \texttt{20pt} if \cs{parindent=0pt}.}
+% sets the minimum acceptable length for paragraphs’ last lines.
%
-% \item[ShortPages:] |\luatypoPageMin=5| sets the minimum acceptable
-% number of lines on a page (chapters’ last page for instance).
+% \item[ShortPages:] |\luatypoPageMin=5| sets the minimum
+% acceptable number of lines on a page (chapters’ last page
+% for instance). Actually, the last line’s vertical position on
+% the page is taken into account so that f.i.\ title pages or
+% pages ending on a picture are not pointed out.
%
% \item[RepeatedHyphens:] |\luatypoHyphMax=2| sets the maximum
% acceptable number of consecutive hyphenated lines.
@@ -546,14 +580,14 @@
% ``Underfull hbox’’ message (when |\tolerance=200| and
% |\hbadness=1000|).
%
-% \item[First/LastWordMatch:] |\luatypoMinFull=3| and
+% \item[First/LastWordMatch:] |\luatypoMinFull=3| and
% |\luatypoMinPart=4| set the minimum number of characters
-% required for a match to be pointed out. With this setting (3
+% required for a match to be pointed out. With this setting (3
% and 4), two occurrences of the word `out’ at the beginning or
% end of two consecutive lines will be highlighted (three chars,
% `in’ wouldn’t match), whereas a line ending with ``full’’ or
% ``overfull’’ followed by one ending with ``underfull’’ will
-% match (four chars): the second occurence of ``full’’ or
+% match (four chars): the second occurence of ``full’’ or
% ``erfull’’ will be highlighted.
%
% \item[EOLShortWords:] this check deals with lines ending with
@@ -598,12 +632,13 @@
% \luatypoSetColor{10}{myred} % First word matches
% \luatypoSetColor{11}{myred} % Last word matches
% \luatypoSetColor{12}{mygrey}% paragraph’s last line nearly full
+% \luatypoSetColor{13}{cyan} % footnotes spread over two pages
% \end{verbatim}
%
% \pkg{lua-typo} loads the \pkg{color} package from the LaTeX graphic
-% bundle. Only named colours can be used by \pkg{lua-typo}, so you can
-% either use the |\definecolor| from \pkg{color} package to define
-% yours (as done in the config file for `mygrey’) or load the
+% bundle. Only named colours can be used by \pkg{lua-typo}, so you
+% can either use the |\definecolor| from \pkg{color} package to
+% define yours (as done in the config file for `mygrey’) or load the
% \pkg{xcolor} package which provides a bunch of named colours.
%
% \clearpage
@@ -611,18 +646,41 @@
%
% \iffalse
%<*sty>
-\NeedsTeXFormat{LaTeX2e}[2020/01/01]
+%% IMPORTANT NOTICE:
+%% For the copyright see the source file `lua-typo.dtx’.
+%%
\ProvidesPackage{lua-typo}
%</sty>
%<*dtx>
\ProvidesFile{lua-typo.dtx}
%</dtx>
%<*dtx|sty>
- [2021/03/14 v.0.32 Daniel Flipo]
+ [2021/05/13 v.0.50 Daniel Flipo]
%</dtx|sty>
%<*sty>
% \fi
%
+% \changes{v0.50}{2021/05/05}{Rollback mechanism used for recovering
+% older versions.}
+%
+% Starting with version~0.50, this package uses the rollback
+% mechanism to provide easier backward compatibility.
+% Rollback version~0.40 is provided for users who would have
+% a LaTeX kernel older than 2021/06/01.
+%
+% \begin{macrocode}
+\ifdefined\DeclareRelease
+ \DeclareRelease{v0.4}{2021-01-01}{lua-typo-2021-04-18.sty}
+ \DeclareCurrentRelease{}{2021-05-13}
+\else
+ \PackageWarning{lua-typo}{Your LaTeX kernel is too old to provide
+ access\MessageBreak to former versions of the lettrine package.%
+ \MessageBreak Anyway, lua-typo requires a LaTeX kernel dated%
+ \MessageBreak 2020-01-01 or newer; reported}
+\fi
+\NeedsTeXFormat{LaTeX2e}[2021/06/01]
+% \end{macrocode}
+%
% This package only runs with LuaLaTeX and requires packages
% \pkg{luatexbase}, \pkg{luacode}, \pkg{luacolor} and
% \pkg{atveryend}.
@@ -684,6 +742,7 @@ luatypo = { }
\DeclareBoolOption[false]{EOLShortWords}
\DeclareBoolOption[false]{FirstWordMatch}
\DeclareBoolOption[false]{LastWordMatch}
+\DeclareBoolOption[false]{FootnoteSplit}
% \end{macrocode}
% Option \opt{All} resets all booleans relative to specific
% typographic checks to \opt{true}.
@@ -695,7 +754,7 @@ luatypo = { }
\LT@EOPHyphenstrue \LT@RepeatedHyphenstrue
\LT@ParLastHyphentrue \LT@EOLShortWordstrue
\LT@FirstWordMatchtrue \LT@LastWordMatchtrue
- \LT@BackParindenttrue
+ \LT@BackParindenttrue \LT@FootnoteSplittrue
}
\ProcessKeyvalOptions{luatypo}
% \end{macrocode}
@@ -790,6 +849,12 @@ luatypo = { }
\else
\directlua{ luatypo.LastWordMatch = false }%
\fi
+ \ifLT@FootnoteSplit
+ \advance\luatypo@options by 1
+ \directlua{ luatypo.FootnoteSplit = true }%
+ \else
+ \directlua{ luatypo.FootnoteSplit = false }%
+ \fi
}
% \end{macrocode}
%
@@ -816,6 +881,7 @@ luatypo = { }
EOLShortWords [false]\MessageBreak
FirstWordMatch [false]\MessageBreak
LastWordMatch [false]\MessageBreak
+ FootnoteSplit [false]\MessageBreak
\MessageBreak
*********************************************%
\MessageBreak Lua-typo [ShowOptions]
@@ -842,7 +908,8 @@ luatypo = { }
% \end{macrocode}
%
% Print the summary of offending pages ---if any--- at the
-% (very) end of document unless option |None| has been selected.
+% (very) end of document and write the report file on disc,
+% unless option |None| has been selected.
%
% \begin{macrocode}
\AtVeryEndDocument{%
@@ -869,6 +936,10 @@ luatypo = { }
end
texio.write_nl('***********************************')
texio.write_nl(' ')
+ local fileout= tex.jobname .. ".typo"
+ local out=io.open(fileout,"w+")
+ out:write(luatypo.buffer)
+ io.close(out)
}%
\fi}
% \end{macrocode}
@@ -927,7 +998,7 @@ luatypo = { }
% \vspace*{\baselineskip}
% \begin{macro}{\luatypoSetColor}
% This is a user-level command to customise the colours highlighting
-% the nine types of possible typographic flaws.
+% the fourteen types of possible typographic flaws.
% The first argument is a number (flaw type), the second the named
% colour associated to it.
% The colour support is based on the \pkg{luacolor} package (color
@@ -950,13 +1021,8 @@ luatypo.single = { }
luatypo.double = { }
luatypo.colortbl = { }
luatypo.pagelist = ""
-
-local hyphcount = 0
-local parlines = 0
-local pagelines = 0
-local pageno = 0
-local prevno = 0
-local pageflag = false
+luatypo.buffer = "List of typographic flaws found for "
+ .. tex.jobname .. ".tex:\string\n\string\n"
local char_to_discard = { }
char_to_discard[string.byte(",")] = true
@@ -980,11 +1046,14 @@ local DISC = node.id("disc")
local GLYPH = node.id("glyph")
local GLUE = node.id("glue")
local KERN = node.id("kern")
+local RULE = node.id("rule")
local HLIST = node.id("hlist")
+local VLIST = node.id("vlist")
local LPAR = node.id("local_par")
local MKERN = node.id("margin_kern")
local PENALTY = node.id("penalty")
-% \end{macrocode}
+local WHATSIT = node.id("whatsit")
+% \end{macrocode}
% Glue subtypes:
% \begin{macrocode}
local USRSKIP = 0
@@ -993,11 +1062,14 @@ local LFTSKIP = 8
local RGTSKIP = 9
local TOPSKIP = 10
local PARFILL = 15
-% \end{macrocode}
+% \end{macrocode}
% Hlist subtypes:
% \begin{macrocode}
local LINE = 1
local BOX = 2
+local INDENT = 3
+local ALIGN = 4
+local EQN = 6
% \end{macrocode}
% Penalty subtypes:
% \begin{macrocode}
@@ -1007,7 +1079,13 @@ local HYPH = 0x2D
% Glyph subtypes:
% \begin{macrocode}
local LIGA = 0x102
+% \end{macrocode}
+% |parline| (current paragraph) must not be reset on every new page!
+% \begin{macrocode}
+local parline = 0
+local dimensions = node.dimensions
+local rangedimensions = node.rangedimensions
local effective_glue = node.effective_glue
local set_attribute = node.set_attribute
local slide = node.slide
@@ -1019,6 +1097,9 @@ local is_glyph = node.is_glyph
% \end{macrocode}
%
+% \changes{v0.32}{2021/03/14}{Better protection against unexpected
+% nil nodes.}
+%
% This auxillary function colours glyphs and discretionaries.
% It requires two arguments: a node and a (named) colour.
%
@@ -1035,55 +1116,119 @@ local color_node = function (node, color)
end
if post then
set_attribute(post,attr,color)
-%<dbg> texio.write_nl('POST=' .. tostring(post.char))
+%<dbg> if pre then
+%<dbg> texio.write(' POST=' .. tostring(post.char))
+%<dbg> else
+%<dbg> texio.write_nl('POST=' .. tostring(post.char))
+%<dbg> end
end
if repl then
set_attribute(repl,attr,color)
-%<dbg> texio.write_nl('REPL=' .. tostring(repl.char))
+%<dbg> if pre or post then
+%<dbg> texio.write(' REPL=' .. tostring(repl.char))
+%<dbg> else
+%<dbg> texio.write_nl('REPL=' .. tostring(repl.char))
+%<dbg> end
end
+%<dbg> if pre or post or repl then
%<dbg> texio.write_nl(' ')
+%<dbg> end
elseif node then
set_attribute(node,attr,color)
end
end
% \end{macrocode}
%
-% This auxillary function colours the content of an |\hbox|.
-% It requires two arguments: a node (the box) and a (named) colour.
+% This auxillary function colours a whole line. It requires two
+% arguments: a line’s node and a (named) colour.\par
+% Digging into nested hlists and vlists is needed f.i.\ to colour
+% aligned equations.
+%
+% \changes{v0.50}{2021/05/05}{Go down deeper into hlists and vlists to
+% colour nodes.}
%
% \begin{macrocode}
-local color_hbox = function (head, color)
+local color_line = function (head, color)
local first = head.head
for n in traverse(first) do
- color_node(n, color)
+ if n.id == HLIST or n.id == VLIST then
+ local ff = n.head
+ for nn in traverse(ff) do
+ if nn.id == HLIST or nn.id == VLIST then
+ local f3 = nn.head
+ for n3 in traverse(f3) do
+ if n3.id == HLIST or n3.id == VLIST then
+ local f4 = n3.head
+ for n4 in traverse(f4) do
+ if n4.id == HLIST or n4.id == VLIST then
+ local f5 = n4.head
+ for n5 in traverse(f5) do
+ if n5.id == HLIST or n5.id == VLIST then
+ local f6 = n5.head
+ for n6 in traverse(f6) do
+ color_node(n6, color)
+ end
+ else
+ color_node(n5, color)
+ end
+ end
+ else
+ color_node(n4, color)
+ end
+ end
+ else
+ color_node(n3, color)
+ end
+ end
+ else
+ color_node(nn, color)
+ end
+ end
+ else
+ color_node(n, color)
+ end
end
end
% \end{macrocode}
%
-% This auxillary function colours a whole line. It requires two
-% arguments: a line’s node and a (named) colour.
+% This function appends a line to a buffer which will be written
+% to file `\cs{jobname.typo}’; it takes four arguments:
+% a string, two numbers (which can be \texttt{nil}) and a flag.
+%
+% \changes{v0.50}{2021/05/13}{Summary of flaws written to file
+% `\cs{jobname.typo}’.}
%
% \begin{macrocode}
-local color_line = function (head, color)
- local first = head.head
- for n in traverse(first) do
- if n and n.id == HLIST and n.subtype == BOX then
- color_hbox(n, color)
- else
- color_node(n, color)
- end
+log_flaw= function (msg, line, colno, footnote)
+ local pageno = tex.getcount("c@page")
+ local prt ="p. " .. pageno
+ if colno then
+ prt = prt .. ", col." .. colno
+ end
+ if line then
+ local l = string.format("%2d, ", line)
+ if footnote then
+ prt = prt .. ", (ftn.) line " .. l
+ else
+ prt = prt .. ", line " .. l
+ end
end
+ prt = prt .. msg
+ luatypo.buffer = luatypo.buffer .. prt .. "\string\n"
end
% \end{macrocode}
%
-% The next three functions deal with ``homeoarchy’’, \emph{i.e.}
+% The next three functions deal with ``homeoarchy'', \emph{i.e.}
% lines beginning or ending with the same (part of) word.
% While comparing two words, the only significant nodes are glyphs
% and ligatures, dicretionnaries other than ligatures, kerns
% (letterspacing) should be discarded.
-% For each word to be compared we build a ``signature’’ made of
+% For each word to be compared we build a ``signature'' made of
% glyphs and split ligatures.
%
+% \changes{v0.32}{2021/03/14}{Experimental code to deal with non
+% standard ligatures.}
+%
% The first function adds a node to a signature of type string.
% It returns the augmented string and its length.
% The last argument is a boolean needed when building a signature
@@ -1096,8 +1241,9 @@ local signature = function (node, string, swap)
local b, id = is_glyph(n)
if b and not char_to_discard[b] then
% \end{macrocode}
-% Punctuation has to be discarded; the French apostrophe (right quote
-% U+2019) has a char code ``out of range’’, we replace it with U+0027;
+% Punctuation has to be discarded; the French apostrophe
+% (right quote U+2019) has a char code ``out of range'',
+% we replace it with U+0027;
% Other glyphs should have char codes less than 0x100 (or 0x180?) or
% be ligatures… standard ones (U+FB00 to U+FB06) are converted using
% table |split_lig|.
@@ -1126,7 +1272,7 @@ local signature = function (node, string, swap)
elseif n and n.id == DISC then
% \end{macrocode}
% Ligatures are split into |pre| and |post| and both parts are
-% stored. In case of \emph{ffl, ffi}, the post part is also
+% stored. In case of \emph{ffl, ffi}, the post part is also
% a ligature…
% \begin{macrocode}
local pre = n.pre
@@ -1163,37 +1309,65 @@ local signature = function (node, string, swap)
end
% \end{macrocode}
%
-% This auxillary function looks for lines ending with the same letters.
-% It requires four arguments: a string (previous line’s signature),
-% a node (the last one on the current line), a (named) colour and a
-% boolean to cancel the coloration in some cases (end of paragraphs).
+% This auxillary function looks for consecutive lines ending with the
+% same letters.
+% It requires four arguments: a string (previous line’s signature),
+% a node (the last one on the current line), a line number and
+% a boolean to cancel checking in some cases (end of paragraphs).
% It prints the matching part at end of linewith with the supplied
% colour and returns the current line’s last word and a boolean (match).
%
+% \changes{v0.32}{2021/03/14}{Functions `check\_last\_word’ and
+% `check\_last\_word’ rewritten.}
+%
+% \changes{v0.50}{2021/05/13}{Homeoarchy detection added for lines
+% starting or ending on \cs{mbox}.}
+%
% \begin{macrocode}
-local check_last_word = function (old, node, color, flag)
+local check_last_word = function (old, node, line, flag)
+ local COLOR = luatypo.colortbl[11]
local match = false
local new = ""
local maxlen = 0
if flag and node then
local swap = true
+ local box, go
% \end{macrocode}
% Step back to the last glyph or discretionary.
% \begin{macrocode}
local lastn = node
- while lastn and lastn.id ~= GLYPH and lastn.id ~= DISC do
+ while lastn and lastn.id ~= GLYPH and lastn.id ~= DISC and
+ lastn.id ~= HLIST do
lastn = lastn.prev
end
% \end{macrocode}
% A signature is built from the last two words on the current line.
% \begin{macrocode}
local n = lastn
+ if n and n.id == HLIST then
+ box = n
+ prev = n.prev
+ lastn = slide(n.head)
+ n = lastn
+ end
while n and n.id ~= GLUE do
maxlen, new = signature (n, new, swap)
n = n.prev
end
if n and n.id == GLUE then
new = new .. "_"
+ go = true
+ elseif box and not n then
+ local p = box.prev
+ if p.id == GLUE then
+ new = new .. "_"
+ n = p
+ else
+ n = box
+ end
+ go = true
+ end
+ if go then
repeat
n = n.prev
maxlen, new = signature (n, new, swap)
@@ -1239,11 +1413,11 @@ local check_last_word = function (old, node, color, flag)
newsub = nsub
end
end
+ pageflag = true
newsub = string.gsub(newsub, '^_', '')
-%<dbg> texio.write_nl('EOLfullmatch=' .. newsub)
-%<msg> texio.write_nl('***EOLMATCH=' .. newsub ..
-%<msg> " on page " .. pageno)
-%<msg> texio.write_nl(' ')
+%<dbg> texio.write_nl('EOLfullmatch=' .. newsub)
+ local msg = "E.O.L. MATCH=" .. newsub
+ log_flaw(msg, line, colno, footnote)
% \end{macrocode}
% Lest's colour the matching string.
% \begin{macrocode}
@@ -1252,39 +1426,63 @@ local check_last_word = function (old, node, color, flag)
local n = lastn
repeat
if n and n.id ~= GLUE then
- color_node(n, color)
+ color_node(n, COLOR)
l, newsub = signature(n, newsub, swap)
- elseif n then
+ elseif n and n.id == GLUE then
newsub = newsub .. "_"
+ elseif not n and box then
+ n = box
+ else
+ break
end
n = n.prev
- until not n or newsub == oldsub or l >= k
+ until newsub == oldsub or l >= k
end
end
- return new, match
+ return new
end
% \end{macrocode}
%
-% Same thing for beginning of lines: check the first two words and
-% compare their signature with the previous line’s.
+% Same thing for beginning of lines: check the first two words
+% and compare their signature with the previous line’s.
%
% \begin{macrocode}
-local check_first_word = function (old, node, color, flag)
+local check_first_word = function (old, node, line, flag)
+ local COLOR = luatypo.colortbl[10]
local match = false
local swap = false
local new = ""
local maxlen = 0
- local start = node
- local n = start
- while n and n.id ~= GLYPH and n.id ~= DISC do
+ local n = node
+ local box, go
+ while n and n.id ~= GLYPH and n.id ~= DISC and
+ (n.id ~= HLIST or n.subtype == INDENT) do
n = n.next
end
+ local start = n
+ if n and n.id == HLIST then
+ box = n
+ start = n.head
+ n = n.head
+ end
while n and n.id ~= GLUE do
maxlen, new = signature (n, new, swap)
n = n.next
end
if n and n.id == GLUE then
new = new .. "_"
+ go = true
+ elseif box and not n then
+ local bn = box.next
+ if bn.id == GLUE then
+ new = new .. "_"
+ n = bn
+ else
+ n = box
+ end
+ go = true
+ end
+ if go then
repeat
n = n.next
maxlen, new = signature (n, new, swap)
@@ -1337,13 +1535,13 @@ local check_first_word = function (old, node, color, flag)
newsub = nsub
end
end
+ pageflag = true
newsub = string.gsub(newsub, '_$', '') --$
%<dbg> texio.write_nl('BOLfullmatch=' .. newsub)
-%<msg> texio.write_nl('***BOLMATCH=' .. newsub ..
-%<msg> " on page " .. pageno)
-%<msg> texio.write_nl(' ')
+ local msg = "B.O.L. MATCH=" .. newsub
+ log_flaw(msg, line, colno, footnote)
% \end{macrocode}
-% Lest's colour the matching string.
+% Lest’s colour the matching string.
% \begin{macrocode}
oldsub = newsub
local newsub = ""
@@ -1351,27 +1549,32 @@ local check_first_word = function (old, node, color, flag)
local n = start
repeat
if n and n.id ~= GLUE then
- color_node(n, color)
+ color_node(n, COLOR)
l, newsub = signature(n, newsub, swap)
- elseif n then
+ elseif n and n.id == GLUE then
newsub = newsub .. "_"
+ elseif not n and box then
+ n = box
+ else
+ break
end
n = n.next
- until not n or newsub == oldsub or l >= k
+ until newsub == oldsub or l >= k
end
end
- return new, match
+ return new
end
% \end{macrocode}
%
% This auxillary function looks for a short word (one or two chars)
% at end of lines, compares it to a given list and colours it if
-% matches. The argument must be a node of type GLYPH, usually
-% the last line’s node.\\
+% matches. The first argument must be a node of type |GLYPH|,
+% usually the last line’s node, the second one is the line number.\\
% TODO: where does ``out of range’’ starts? U+0100? U+0180?
%
% \begin{macrocode}
-local check_regexpr = function (glyph)
+local check_regexpr = function (glyph, line)
+ local pageno = tex.getcount("c@page")
local COLOR = luatypo.colortbl[3]
local lang = glyph.lang
local match = false
@@ -1387,10 +1590,10 @@ local check_regexpr = function (glyph)
if lchar and lchar < 0x100 and previous and previous.id == GLUE then
match = string.find(luatypo.single[lang], string.char(lchar))
if match then
+ pageflag = true
+ local msg = "RGX MATCH=" .. string.char(lchar)
+ log_flaw(msg, line, colno, footnote)
color_node(glyph,COLOR)
-%<msg> texio.write_nl('***RGXMATCH=' .. string.char(lchar) ..
-%<msg> " on page " .. pageno)
-%<msg> texio.write_nl(' ')
end
end
end
@@ -1408,11 +1611,11 @@ local check_regexpr = function (glyph)
local pattern = string.char(pchar) .. string.char(lchar)
match = string.find(luatypo.double[lang], pattern)
if match then
+ pageflag = true
+ local msg = "RGX MATCH=" .. pattern
+ log_flaw(msg, line, colno, footnote)
color_node(previous,COLOR)
color_node(glyph,COLOR)
-%<msg> texio.write_nl('***RGXMATCH=' .. pattern ..
-%<msg> " on page " .. pageno)
-%<msg> texio.write_nl(' ')
end
end
% \end{macrocode}
@@ -1423,27 +1626,27 @@ local check_regexpr = function (glyph)
if pprev and pprev.id == GLYPH then
local pchar, id = is_glyph(pprev)
local ppprev = pprev.prev
- if pchar and pchar < 0x100 and ppprev and ppprev.id == GLUE then
+ if pchar and pchar < 0x100 and
+ ppprev and ppprev.id == GLUE then
local pattern = string.char(pchar) .. string.char(lchar)
match = string.find(luatypo.double[lang], pattern)
if match then
+ pageflag = true
+ local msg = "RGX MATCH=" .. pattern
+ log_flaw(msg, line, colno, footnote)
color_node(pprev,COLOR)
color_node(glyph,COLOR)
-%<msg> texio.write_nl('***RGXMATCH=' .. pattern ..
-%<msg> " on page " .. pageno)
-%<msg> texio.write_nl(' ')
end
end
end
end
end
- return match
end
% \end{macrocode}
%
% This auxillary function prints the first part of an hyphenated word
% up to the discretionary, with a supplied colour.
-% It requires two arguments: a DISC node and a (named) colour.
+% It requires two arguments: a |DISC| node and a (named) colour.
%
% \begin{macrocode}
local show_pre_disc = function (disc, color)
@@ -1453,149 +1656,297 @@ local show_pre_disc = function (disc, color)
n = n.prev
end
return n
+ end
+% \end{macrocode}
+%
+% This auxillary function scans the `vlists’ in search of the page body.
+% It returns the corresponding node or nil in case of failure.
+%
+% \changes{v0.50}{2021/05/02}{New function `get\_pagebody’ required for
+% callback `pre\_shipout\_filter’.}
+%
+% \begin{macrocode}
+local get_pagebody = function (head)
+ local textht = tex.getdimen("textheight")
+ local fn = head.list
+ local body = nil
+ repeat
+ fn = fn.next
+ until fn.id == VLIST and fn.height > 0
+%<dbg> texio.write_nl(' ht=' .. fn.height/65536 .. 'pt')
+%<dbg> texio.write(' dp=' .. fn.depth/65536 .. 'pt')
+ first = fn.list
+ for n in traverse_id(VLIST,first) do
+ if n.subtype == 0 and n.height == textht then
+%<dbg> texio.write_nl(' BODY: ' .. n.height/65536 .. 'pt')
+ body = n
+ break
+ else
+%<dbg> texio.write_nl(' ht=' .. n.height/65536 .. 'pt')
+%<dbg> texio.write_nl(' dp=' .. n.depth/65536 .. 'pt')
+ first = n.list
+ for n in traverse_id(VLIST,first) do
+ if n.subtype == 0 and n.height == textht then
+%<dbg> texio.write_nl(' BODY: ' .. n.height/65536 .. 'pt')
+ body = n
+ break
+ end
+ end
+ end
+ end
+ if not body then
+ texio.write_nl('***lua-typo ERROR: PAGE BODY *NOT* FOUND!***')
+ end
+ return body
end
% \end{macrocode}
%
-% This is the main function which will be added to the
-% |"pre_output_filter"| callback unless option |None| is selected.
+% This auxillary function scans the current `vlist’ in search
+% of a |\footnoterule| (kern, rule, kern, totalheight=0).
+% It returns |true| if found, false othewise.
+%
+% \changes{v0.50}{2021/05/02}{New function `footnoterule\_ahead’.}
%
% \begin{macrocode}
-luatypo.check_page = function (head)
+local footnoterule_ahead = function (head, debug)
+ local n = head
+ local flag = false
+ if n and n.id == KERN and n.subtype == 1 then
+ local htr = n.kern
+ local ht1, ht2, ht3
+%<dbg> if debug then
+%<dbg> ht1 = string.format("%.2fpt", n.kern/65536)
+%<dbg> end
+ n = n.next
+ if n and n.id == RULE and n.subtype == 0 then
+ htr = htr + n.height
+%<dbg> if debug then
+%<dbg> ht2 = string.format("%.2fpt", n.height/65536)
+%<dbg> end
+ n = n.next
+ if n and n.id == KERN and n.subtype == 1 then
+ htr = htr + n.kern
+%<dbg> if debug then
+%<dbg> ht3 = string.format("%.2fpt", n.kern/65536)
+%<dbg> texio.write_nl(' ')
+%<dbg> texio.write_nl('KERN height: ' .. ht1)
+%<dbg> texio.write(' RULE height: ' .. ht2)
+%<dbg> texio.write(' KERN height: ' .. ht3)
+%<dbg> texio.write_nl('TOTAL height: ' .. htr .. 'sp')
+%<dbg> end
+ if htr == 0 then
+ flag = true
+%<dbg> if debug then
+%<dbg> texio.write(' => footnoterule found!')
+%<dbg> end
+ end
+ end
+ end
+ end
+ return flag
+end
+% \end{macrocode}
+%
+% This function scans the page body (or each column) in search of
+% typographical flaws.
+%
+% \changes{v0.40}{2021/04/18}{Title pages, pages with figures and/or
+% tables may not be empty pages: check `vpos’ last line’s position.}
+%
+% \begin{macrocode}
+check_vtop = function (head, colno)
local PAGEmin = luatypo.PAGEmin
local HYPHmax = luatypo.HYPHmax
local LLminWD = luatypo.LLminWD
local BackPI = luatypo.BackPI
local BackFuzz = luatypo.BackFuzz
- local BackParindent = luatypo.BackParindent
- local ShortLines = luatypo.ShortLines
- local ShortPages = luatypo.ShortPages
- local OverfullLines = luatypo.OverfullLines
- local UnderfullLines = luatypo.UnderfullLines
- local Widows = luatypo.Widows
- local Orphans = luatypo.Orphans
- local EOPHyphens = luatypo.EOPHyphens
+ local BackParindent = luatypo.BackParindent
+ local ShortLines = luatypo.ShortLines
+ local ShortPages = luatypo.ShortPages
+ local OverfullLines = luatypo.OverfullLines
+ local UnderfullLines = luatypo.UnderfullLines
+ local Widows = luatypo.Widows
+ local Orphans = luatypo.Orphans
+ local EOPHyphens = luatypo.EOPHyphens
local RepeatedHyphens = luatypo.RepeatedHyphens
- local FirstWordMatch = luatypo.FirstWordMatch
- local ParLastHyphen = luatypo.ParLastHyphen
- local EOLShortWords = luatypo.EOLShortWords
- local LastWordMatch = luatypo.LastWordMatch
- local Stretch = math.max(luatypo.Stretch/100,1)
-
+ local FirstWordMatch = luatypo.FirstWordMatch
+ local ParLastHyphen = luatypo.ParLastHyphen
+ local EOLShortWords = luatypo.EOLShortWords
+ local LastWordMatch = luatypo.LastWordMatch
+ local FootnoteSplit = luatypo.FootnoteSplit
+ local Stretch = math.max(luatypo.Stretch/100,1)
+ local blskip = tex.getglue("baselineskip")
+ local pageno = tex.getcount("c@page")
+ local vpos_min = PAGEmin * blskip
+ vpos_min = vpos_min * 1.5
+ local vpos = 0
+ local pageflag = false
+ local body_bottom = false
+ local page_bottom = false
+ local first_bot = true
+ local footnote = false
+ local ftnsplit = false
local orphanflag = false
local widowflag = false
local lwhyphflag = false
- local match1 = false
- local match2 = false
+ local pageshort = false
local firstwd = ""
local lastwd = ""
+ local hyphcount = 0
+ local pageline = 0
+ local ftnline = 0
+ local line = 0
+% \end{macrocode}
+% The main loop scans the content of the |\vtop| holding the page
+% (or column) body, footnotes included.
+% The vertical position of the current node is stored in the |vpos|
+% dimension (integer in `sp’ units).
+% \begin{macrocode}
while head do
local nextnode = head.next
- local prevnode = head.prev
- local pprevnode = nil
- if prevnode then
- pprevnode = prevnode.prev
- end
% \end{macrocode}
-% If the current node is a glue of type topskip, we are starting new
-% page, let’s reset some counters and flags : |pageflag| will be set
-% to |true| if a possible typographic flaw is found on this page,
-% and trigger the addition of this page number to the summary list.
-% |hyphcount| hold the number the consecutive hyphenated lines.
-% \begin{macrocode}
- if head.id == GLUE and head.subtype == TOPSKIP then
- pageno = tex.getcount("c@page")
- hyphcount = 0
- if pageno > prevno then
- pageflag = false
- pagelines = 0
- match1 = false
+% If a |\footnoterule| is found, set the |footnote| flag and reset
+% some counters and flags for the coming footnotes.
+% \begin{macrocode}
+ if not footnote and head.id == KERN and head.subtype == 1 then
+ if footnoterule_ahead(head, true) then
+ footnote = true
+ ftnline = 0
+ body_bottom = false
+ orphanflag = false
+ lwhyphflag = false
+ hyphcount = 0
firstwd = ""
lastwd = ""
- prevno = pageno
- end
- elseif head.id == HLIST and head.subtype == LINE then
-% \end{macrocode}
-% The current node is a line, |first| is the line’s first node.
-% Skip margin kern or leftskip if any.
-% \begin{macrocode}
- local first = head.head
- if first.id == MKERN or
- (first.id == GLUE and first.subtype == LFTSKIP) then
- first = first.next
+ else
+ vpos = vpos + head.kern
end
- pagelines = pagelines + 1
- local ListItem = false
+ elseif head.id == HLIST and head.subtype == LINE and
+ (head.height > 0 or head.depth > 0) then
% \end{macrocode}
-% Is this line really a text line (one glyph at least)?
+% This is a text line, increment counters |pageline| or
+% |ftnline| and |line| (for |log_flaw|).
% \begin{macrocode}
- local textline = false
- if first.id == GLYPH then
- textline = true
+ if footnote then
+ ftnline = ftnline + 1
+ line = ftnline
else
- local n = first
- repeat
- n = n.next
- if n and n.id == GLYPH then
- textline = true
- break
- end
- until not n or (n.id == GLUE and n.subtype == RGTSKIP)
+ pageline = pageline + 1
+ line = pageline
end
% \end{macrocode}
-% Is this line overfull or underfull?
+% Is it overfull or underfull?
+% \changes{v0.50}{2021/05/13}{Detection of overfull boxes fixed: the
+% former code didn’t work for typewriter fonts.}
% \begin{macrocode}
- if head.glue_set == 1 and head.glue_sign == 2 and
- head.glue_order == 0 and OverfullLines then
-%<msg> texio.write_nl('***OVERFULL line on page ' .. pageno)
-%<msg> texio.write_nl(' ')
+ local first = head.head
+ local hmax = head.width + tex.hfuzz
+ local w,h,d = dimensions(1,2,0, first)
+ if w > hmax and OverfullLines then
pageflag = true
+ local wpt = string.format("%.2fpt", (w-head.width)/65536)
+ local msg = "OVERFULL line " .. wpt
+ log_flaw(msg, line, colno, footnote)
local COLOR = luatypo.colortbl[7]
color_line (head, COLOR)
- elseif head.glue_set >= Stretch and head.glue_sign == 1 and
- head.glue_order == 0 and UnderfullLines then
-%<msg> texio.write_nl('***UNDERFULL line on page ' ..
-%<msg> tex.getcount("c@page"))
-%<msg> texio.write_nl(' ')
- local COLOR = luatypo.colortbl[8]
+ elseif head.glue_set > Stretch and head.glue_sign == 1 and
+ head.glue_order == 0 and UnderfullLines then
pageflag = true
+ local s = string.format("%.0f%s", 100*head.glue_set, "%")
+ local msg = "UNDERFULL line stretch=" .. s
+ log_flaw(msg, line, colno, footnote)
+ local COLOR = luatypo.colortbl[8]
color_line (head, COLOR)
end
% \end{macrocode}
+% Let’s update |vpos| and check if the current line is the last one
+% of the page body; this requires to look ahead \emph{now} for the
+% next nodes in the `vlist’ as this information is needed to decide
+% about orphans, last page’s word hyphenated, etc.
+% \begin{macrocode}
+ vpos = vpos + head.height + head.depth
+ local n = head.next
+ while n and
+ (n.id == GLUE or n.id == PENALTY or n.id == WHATSIT) do
+ n = n.next
+ end
+% \end{macrocode}
+% Is this line the last one on the current page? …
+% \begin{macrocode}
+ if not n then
+ if footnote then
+ page_bottom = true
+ else
+ page_bottom = true
+ body_bottom = true
+ end
+% \end{macrocode}
+% or the last one before |\footnoterule|?
+% \begin{macrocode}
+ elseif footnoterule_ahead(n, false) then
+ body_bottom = true
+ end
+% \end{macrocode}
+% Set flag |ftnsplit| to |true| on every page’s last line.
+% This flag will be reset to false if the current line ends a
+% paragraph.
+% \begin{macrocode}
+ if footnote and page_bottom then
+ ftnsplit = true
+ end
+% \end{macrocode}
+% The current node is a line, |first| is the line’s first node.
+% Skip margin kern and/or leftskip if any.
+%
+% \changes{v0.40}{2021/04/18}{Both MKERN and LFTSKIP may occur on
+% the same line.}
+%
+% \changes{v0.40}{2021/04/18}{All hlists of subtype LINE now
+% count as a pageline.}
+%
+% \begin{macrocode}
+ while first.id == MKERN or
+ (first.id == GLUE and first.subtype == LFTSKIP) do
+ first = first.next
+ end
+ local ListItem = false
+% \end{macrocode}
% Now let’s analyse the beginning of the current line.
% \begin{macrocode}
if first.id == LPAR then
% \end{macrocode}
-% It starts a paragraph…
+% It starts a paragraph… Reset |parline| except in footnotes
+% (|parline| and |pageline| counts are for ``body'' \emph{only},
+% they are frozen in footnotes).
% \begin{macrocode}
hyphcount = 0
- parlines = 1
- if not nextnode then
+ if not footnote then
+ parline = 1
+ end
+ if body_bottom then
% \end{macrocode}
-% No more nodes: we are at the page bottom, this ligne is
-% an orphan (unless it is the unique line of the paragraph)… see below.
+% We are at the page bottom (footnotes excluded), this ligne is
+% an orphan (unless it is the unique line of the paragraph, this
+% will be checked later when scanning the end of line).
% \begin{macrocode}
orphanflag = true
end
% \end{macrocode}
-% List items begin with LPAR followed by an hbox.
+% List items begin with |LPAR| followed by an hbox.
% \begin{macrocode}
local nn = first.next
- if nn and nn.id == HLIST and nn.subtype == BOX then
- ListItem = true
+ if nn and nn.id == HLIST and nn.subtype == BOX then
+ ListItem = true
end
- else
- parlines = parlines + 1
+ elseif not footnote then
+ parline = parline + 1
end
% \end{macrocode}
% Let’s track lines beginning with the same word (except lists).
% \begin{macrocode}
if FirstWordMatch then
local flag = not ListItem
- local COLOR = luatypo.colortbl[10]
- firstwd, match1 = check_first_word(firstwd, first, COLOR, flag)
- if match1 then
- pageflag = true
- end
+ firstwd = check_first_word(firstwd, first, line, flag)
end
% \end{macrocode}
% Let’s check the end of line: |ln| (usually a rightskip) and |pn|
@@ -1605,15 +1956,17 @@ luatypo.check_page = function (head)
local pn = ln.prev
if pn and pn.id == GLUE and pn.subtype == PARFILL then
% \end{macrocode}
-% The paragraph ends with this line, it is not an orphan then…
+% CASE 1: this line ends the paragraph, reset |ftnsplit| and |orphan|
+% flags to false…
% \begin{macrocode}
hyphcount = 0
+ ftnsplit = false
orphanflag = false
% \end{macrocode}
% but it is a widow if it is the page’s first line and it does’nt
% start a new paragraph.\\ Orphans and widows will be colored later.
% \begin{macrocode}
- if pagelines == 1 and parlines > 1 then
+ if pageline == 1 and parline > 1 then
widowflag = true
end
% \end{macrocode}
@@ -1622,18 +1975,19 @@ luatypo.check_page = function (head)
local PFskip = effective_glue(pn,head)
if ShortLines then
local llwd = tex.hsize - PFskip
-%<dbg> local PFskip_pt = PFskip/65536
-%<dbg> local llwd_pt = llwd/65536
-%<dbg> texio.write_nl('PFskip= ' .. PFskip_pt .. ' pt')
-%<dbg> texio.write_nl('llwd= ' .. llwd_pt .. ' pt')
+%<dbg> local PFskip_pt = string.format("%.1fpt", PFskip/65536)
+%<dbg> local llwd_pt = string.format("%.1fpt", llwd/65536)
+%<dbg> texio.write_nl('PFskip= ' .. PFskip_pt)
+%<dbg> texio.write(' llwd= ' .. llwd_pt)
% \end{macrocode}
% |llwd| is the line’s length. Is it too short?
% \begin{macrocode}
if llwd < LLminWD then
-%<msg> texio.write_nl('***Last line too short, page ' .. pageno)
-%<msg> texio.write_nl(' ')
pageflag = true
- local COLOR = luatypo.colortbl[6]
+ local msg = "SHORT LINE: " ..
+ string.format("%.0fpt", llwd/65536)
+ log_flaw(msg, line, colno, footnote)
+ local COLOR = luatypo.colortbl[6]
local attr = oberdiek.luacolor.getattribute()
% \end{macrocode}
% let’s colour the whole line.
@@ -1645,10 +1999,11 @@ luatypo.check_page = function (head)
% Is this line nearly full? (ending too close to the right margin)
% \begin{macrocode}
if BackParindent and PFskip < BackPI and PFskip > BackFuzz then
-%<msg> texio.write_nl('***Last line nearly full, page ' .. pageno)
-%<msg> texio.write_nl(' ')
pageflag = true
- local COLOR = luatypo.colortbl[12]
+ local msg = "LINE NEARLY FULL: missing " ..
+ string.format("%.1fpt", PFskip/65536)
+ log_flaw(msg, line, colno, footnote)
+ local COLOR = luatypo.colortbl[12]
local attr = oberdiek.luacolor.getattribute()
color_line (head, COLOR)
end
@@ -1656,38 +2011,30 @@ luatypo.check_page = function (head)
% Does the last word and the one on the previous line match?
% \begin{macrocode}
if LastWordMatch then
- local COLOR = luatypo.colortbl[11]
local flag = textline
if PFskip > BackPI then
flag = false
end
- lastwd, match1 = check_last_word(lastwd, pn, COLOR, flag)
- if match1 then
- pageflag = true
- end
+ lastwd = check_last_word(lastwd, pn, line, flag)
end
elseif pn and pn.id == DISC then
% \end{macrocode}
-% The current line ends with an hyphen.
+% CASE 2: the current line ends with an hyphen.
% \begin{macrocode}
hyphcount = hyphcount + 1
if LastWordMatch then
- local COLOR = luatypo.colortbl[11]
- lastwd, match1 = check_last_word(lastwd, ln, COLOR, true)
- if match1 then
- pageflag = true
- end
+ lastwd = check_last_word(lastwd, ln, line, true)
end
if hyphcount > HYPHmax and RepeatedHyphens then
local COLOR = luatypo.colortbl[2]
local pg = show_pre_disc (pn,COLOR)
pageflag = true
-%<msg> texio.write_nl('***HYPH issue page ' .. pageno)
-%<msg> texio.write_nl(' ')
+ local msg = "REPEATED HYPHENS: more than " .. HYPHmax
+ log_flaw(msg, line, colno, footnote)
end
- if not nextnode and EOPHyphens then
+ if (page_bottom or body_bottom) and EOPHyphens then
% \end{macrocode}
-% No more nodes: this hyphen occurs on the page last line.
+% This hyphen occurs on the page’s last line (body or footnote).
% \begin{macrocode}
lwhyphflag = true
end
@@ -1697,64 +2044,38 @@ luatypo.check_page = function (head)
% a `linebreak penalty’, the next one is a `baseline skip’ and the
% node after a `hlist of subtype line’ with |glue_order=2|.
% \begin{macrocode}
- local nnnode = nextnode.next
- local nnnnode = nil
- if nnnode and nnnode.next then
- nnnnode = nnnode.next
- if nnnnode and nnnnode.id == HLIST and
- nnnnode.subtype == 1 and nnnnode.glue_order == 2 then
+ local nn = nextnode.next
+ local nnn = nil
+ if nn and nn.next then
+ nnn = nn.next
+ if nnn.id == HLIST and nnn.subtype == LINE and
+ nnn.glue_order == 2 then
+ pageflag = true
+ local msg = "HYPHEN on next to last line"
+ log_flaw(msg, line, colno, footnote)
local COLOR = luatypo.colortbl[0]
local pg = show_pre_disc (pn,COLOR)
- pageflag = true
end
end
end
- elseif pn and pn.id == GLYPH then
% \end{macrocode}
-% \enlargethispage*{\baselineskip}
-% The current line ends with a character, reset |hyphcount| and
-% compare the end of line’s word with the one on previous line.
+% CASE 3: the current line ends with anything else (|MKERN|,
+% |GLYPH|, |HLIST|, etc.), reset |hyphcount|, perform checks
+% for `LastWordMatch’ and for `EOLShortWords’.
% \begin{macrocode}
+ else
hyphcount = 0
- if LastWordMatch then
- local COLOR = luatypo.colortbl[11]
- lastwd, match1 = check_last_word(lastwd, pn, COLOR, true)
+ if LastWordMatch and pn then
+ lastwd = check_last_word(lastwd, pn, line, true)
end
-% \end{macrocode}
-% Look for a short unwanted short word at the end of the current
-% line.
-% \begin{macrocode}
if EOLShortWords then
- match2 = check_regexpr(pn)
- end
- if match1 or match2 then
- pageflag = true
- end
-% \end{macrocode}
-% Microtype sometimes adds a margin kern at the right margin,
-% |check_regexpr| has to operate on the previous node then.
-% \begin{macrocode}
- elseif pn and pn.id == MKERN then
- hyphcount = 0
- local ppn = pn.prev
- if ppn and ppn.id == GLYPH then
- if LastWordMatch then
- local COLOR = luatypo.colortbl[11]
- lastwd, match1 = check_last_word(lastwd, pn, COLOR, true)
+ while pn and pn.id ~= GLYPH and pn.id ~= HLIST do
+ pn = pn.prev
end
- if EOLShortWords then
- match2 = check_regexpr(ppn)
- end
- if match1 or match2 then
- pageflag = true
+ if pn and pn.id == GLYPH then
+ check_regexpr(pn,line)
end
end
-% \end{macrocode}
-% If the current line ends with anything else (no hyphen),
-% reset |hyphcount|.
-% \begin{macrocode}
- else
- hyphcount = 0
end
% \end{macrocode}
% Colour the whole line if is is a widow.
@@ -1762,59 +2083,221 @@ luatypo.check_page = function (head)
if widowflag and Widows then
pageflag = true
widowflag = false
-%<msg> texio.write_nl('***WIDOW on top of page ' .. pageno)
-%<msg> texio.write_nl(' ')
+ local msg = "WIDOW"
+ log_flaw(msg, line, colno, footnote)
local COLOR = luatypo.colortbl[4]
color_line (head, COLOR)
end
% \end{macrocode}
-% Colour the whole line if is is a orphan.
+% Colour the whole line if is is a orphan or footenote continuing
+% on the next page.
% \begin{macrocode}
if orphanflag and Orphans then
-%<msg> texio.write_nl('***ORPHAN at bottom of page ' .. pageno)
-%<msg> texio.write_nl(' ')
pageflag = true
+ local msg = "ORPHAN"
+ log_flaw(msg, line, colno, footnote)
local COLOR = luatypo.colortbl[5]
color_line (head, COLOR)
end
+ if ftnsplit and FootnoteSplit then
+ pageflag = true
+ local msg = "FOOTNOTE SPLIT"
+ log_flaw(msg, line, colno, footnote)
+ local COLOR = luatypo.colortbl[13]
+ color_line (head, COLOR)
+ end
% \end{macrocode}
% Colour (differently) the last word if hyphenated.
% \begin{macrocode}
if lwhyphflag and EOPHyphens then
-%<msg> texio.write_nl('***LAST WORD SPLIT page ' .. pageno)
-%<msg> texio.write_nl(' ')
pageflag = true
+ local msg = "LAST WORD SPLIT"
+ log_flaw(msg, line, colno, footnote)
local COLOR = luatypo.colortbl[1]
local pg = show_pre_disc (pn,COLOR)
end
+ elseif head.id == HLIST and
+ (head.subtype == EQN or head.subtype == ALIGN) and
+ (head.height > 0 or head.depth > 0) then
% \end{macrocode}
-% Track empty pages: check the number of lines at end of page.
-% Widows are already detected; get the last line and colour it.
-% NOTE: there are usually two consecutive nodes of type 12-0 at
-% end of pages…
+%
+% \changes{v0.50}{2021/05/05}{Consider displayed and aligned equations
+% too for overfull boxes.}
+%
+% This line is a displayed or aligned equation.
+% Let’s update |vpos| and the line number.
% \begin{macrocode}
- elseif not nextnode and head.id == GLUE and
- head.subtype == USRSKIP then
- if pagelines > 1 and pagelines < PAGEmin and ShortPages then
- pageflag = true
- local COLOR = luatypo.colortbl[9]
-%<msg> texio.write_nl('***Only ' .. pagelines ..
-%<msg> ' lines on page ' .. pageno)
-%<msg> texio.write_nl(' ')
- local n = head
- while n and (n.id ~= HLIST or n.subtype ~= LINE) do
- n = n.prev
+ vpos = vpos + head.height + head.depth
+ if footnote then
+ ftnline = ftnline + 1
+ line = ftnline
+ else
+ pageline = pageline + 1
+ line = pageline
+ end
+% \end{macrocode}
+% Let’s check for an ``Overfull box''. For a displayed equation
+% it is straightforward. A set of aligned equations all have the
+% same (maximal) width; in order to avoid highlighting the whole
+% set, we have to look for glues at the end of embedded `hlists’.
+% \begin{macrocode}
+ local fl = true
+ local wd = 0
+ local hmax = 0
+ if head.subtype == EQN then
+ local f = head.list
+ wd = rangedimensions(head,f)
+ hmax = head.width + tex.hfuzz
+ else
+ wd = head.width
+ hmax = tex.getdimen("linewidth") + tex.hfuzz
+ end
+ if wd > hmax and OverfullLines then
+ if head.subtype == ALIGN then
+ local first = head.list
+ for n in traverse_id(HLIST, first) do
+ local last = slide(n.list)
+ if last.id == GLUE and last.subtype == USER then
+ wd = wd - effective_glue(last,n)
+ if wd <= hmax then fl = false end
+ end
+ end
end
- if n then
- color_line(n, COLOR)
+ if fl then
+ pageflag = true
+ local w = wd - hmax + tex.hfuzz
+ local wpt = string.format("%.2fpt", w/65536)
+ local msg = "OVERFULL equation " .. wpt
+ log_flaw(msg, line, colno, footnote)
+ local COLOR = luatypo.colortbl[7]
+ color_line (head, COLOR)
end
end
+% \end{macrocode}
+% We also need to set flag |body_bottom| and to increment the
+% |pageline| counter to track empty pages.
+% \begin{macrocode}
+ local n = head.next
+ while n and (n.id == GLUE or n.id == PENALTY or
+ n.id == WHATSIT or n.id == VLIST) do
+ n = n.next
+ end
+ if not n then
+ page_bottom = true
+ body_bottom = true
+ elseif footnoterule_ahead(n, false) then
+ body_bottom = true
+ end
+% \end{macrocode}
+% This is a |\vbox|, let’s update |vpos|.
+% \begin{macrocode}
+ elseif head.id == VLIST then
+ vpos = vpos + head.height + head.depth
+% \end{macrocode}
+% Track empty pages: check the number of lines at end of page,
+% in case this number is low, \emph{and} |vpos| is less than
+% |vpos_min|, fetch the last line and colour it.\\
+% NOTE1: |effective_glue| requires a `parent’ node, as pointed out by
+% Marcel Krüger on S.E., this implies using |pre_shipout_filter|
+% instead of |pre_output_filter|.\\
+% NOTE2: Widows are already detected, skip them here; there are
+% usually two consecutive nodes of type 12-0 at end of pages…
+% \begin{macrocode}
+ elseif body_bottom and head.id == GLUE and head.subtype == 0 then
+ if first_bot then
+%<dbg> local vpos_pt = string.format("%.1fpt", vpos/65536)
+%<dbg> local vmin_pt = string.format("%.1fpt", vpos_min/65536)
+%<dbg> texio.write_nl('pageline=' .. pageline)
+%<dbg> texio.write_nl('vpos=' .. vpos_pt)
+%<dbg> texio.write(' vpos_min=' .. vmin_pt)
+%<dbg> if page_bottom then
+%<dbg> local tht = tex.getdimen("textheight")
+%<dbg> local tht_pt = string.format("%.1fpt", tht/65536)
+%<dbg> texio.write(' textheight=' .. tht_pt)
+%<dbg> end
+%<dbg> texio.write_nl(' ')
+ if pageline > 1 and pageline < PAGEmin and ShortPages then
+ pageshort = true
+ end
+ if pageshort and vpos < vpos_min then
+ pageflag = true
+ local msg = "SHORT PAGE: only " .. pageline .. " lines"
+ log_flaw(msg, line, colno, footnote)
+ local COLOR = luatypo.colortbl[9]
+ local n = head
+ repeat
+ n = n.prev
+ until n.id == HLIST
+ color_line (n, COLOR)
+ end
+ first_bot = false
+ end
+% \end{macrocode}
+% Increment |vpos| on other vertical glues.
+% \begin{macrocode}
+ elseif head.id == GLUE then
+ vpos = vpos + effective_glue(head,body)
end
head = nextnode
end
+ return pageflag
+end
+% \end{macrocode}
+%
+% This is the main function which will be added to the
+% |pre_shipout_filter| callback unless option |None| is selected.
+% It executes |get_pagebody|, then scans the page body for possible
+% columns (multi column page).
+%
+% \begin{macrocode}
+luatypo.check_page = function (head)
+ local pageno = tex.getcount("c@page")
+ local pageflag = false
+ local n2, n3, col, colno
+ local body = get_pagebody(head)
+ local first = body.list
+%<dbg> texio.write_nl('body.id=' .. tostring(node.type(body.id)))
+%<dbg> texio.write('-' .. body.subtype)
+%<dbg> texio.write_nl(' ')
+%<dbg> texio.write_nl('first.id=' .. tostring(node.type(first.id)))
+%<dbg> texio.write('-' .. first.subtype)
+%<dbg> texio.write_nl(' ')
+ if first.id == HLIST and first.subtype == 2 then
+% \end{macrocode}
+% Two or more columns, each one is boxed in an hlist.
+% Run |check_vtop| on every column.
+% \begin{macrocode}
+ n2 = first.list
+ colno = 0
+ for n in traverse_id(HLIST, n2) do
+%<dbg> texio.write_nl('n.id=' .. tostring(node.type(n.id)))
+%<dbg> texio.write('-' .. n.subtype)
+%<dbg> texio.write(' ht=' .. n.height)
+%<dbg> texio.write_nl(' ')
+ if n.id == HLIST and n.subtype == 2 then
+ n3 = n.list
+%<dbg> texio.write_nl('n3.id=' .. tostring(node.type(n3.id)))
+%<dbg> texio.write('-' .. n3.subtype)
+%<dbg> texio.write(' ht=' .. n3.height)
+%<dbg> texio.write_nl(' ')
+ col = n3.list
+ colno = colno + 1
+ pageflag = check_vtop(col,colno)
+ end
+ end
+ elseif body.id == VLIST and body.subtype == 0 then
+% \end{macrocode}
+% Single column, run |check_vtop| on the top vlist.
+% \begin{macrocode}
+ col = body.list
+ pageflag = check_vtop(col,colno)
+ end
% \end{macrocode}
% Add this page number to the summary if any flaw has been found on it.
% Skip duplicates.
+%
+% \changes{v0.32}{2021/03/14}{Remove duplicates in the summary of pages.}
+%
% \begin{macrocode}
if pageflag then
local pl = luatypo.pagelist
@@ -1829,22 +2312,29 @@ return luatypo.check_page
\end{luacode}
% \end{macrocode}
%
-% Add the |luatypo.check_page| function to the |pre_output_filter|
-% callback, unless option |None| is selected ; remember that the
-% |None| boolean’s value is forwarded to Lua `AtEndOfPackage’…
+% Add the |luatypo.check_page| function to the |pre_shipout_filter|
+% callback (with priority 1 for color attributes to be effective),
+% unless option |None| is selected ; remember that the |None|
+% boolean’s value is forwarded to Lua `AtEndOfPackage’…
+%
+% \changes{v0.50}{2021/05/02}{Callback `pre\_output\_filter’ replaced
+% by `pre\_shipout\_filter’, in the later the material is not boxed
+% yet and footnotes are not visible.}
+%
% \begin{macrocode}
\AtEndOfPackage{%
\directlua{
if not luatypo.None then
luatexbase.add_to_callback
- ("pre_output_filter",luatypo.check_page,"check_page")
+ ("pre_shipout_filter",luatypo.check_page,"check_page",1)
end
}
}
% \end{macrocode}
%
-% Load a local config file if present in LaTeX’s search path.
+% Load a local config file if present in LaTeX’s search path.\\
% Otherwise, set reasonnable defaults.
+% \enlargethispage*{\baselineskip}
% \begin{macrocode}
\InputIfFileExists{lua-typo.cfg}%
@@ -1855,7 +2345,7 @@ return luatypo.check_page
\definecolor{myred}{rgb}{1,0.55,0}
\luatypoSetColor0{red}% Paragraph last full line hyphenated
\luatypoSetColor1{red}% Page last word hyphenated
- \luatypoSetColor2{red}% Hyphens ending two many consecutive lines
+ \luatypoSetColor2{red}% Hyphens on to many consecutive lines
\luatypoSetColor3{red}% Short word at end of line
\luatypoSetColor4{cyan}% Widow
\luatypoSetColor5{cyan}% Orphan
@@ -1866,9 +2356,11 @@ return luatypo.check_page
\luatypoSetColor{10}{myred}% First word matches
\luatypoSetColor{11}{myred}% Last word matches
\luatypoSetColor{12}{mygrey}% Paragraph ending on a nearly full line
+ \luatypoSetColor{13}{cyan}% Footnote split
\luatypoBackPI=1em\relax
\luatypoBackFuzz=2pt\relax
- \luatypoLLminWD=2\parindent\relax
+ \ifdim\parindent=0pt \luatypoLLminWD=20pt\relax
+ \else\luatypoLLminWD=2\parindent\relax\fi
\luatypoStretchMax=200\relax
\luatypoHyphMax=2\relax
\luatypoPageMin=5\relax
@@ -1879,8 +2371,7 @@ return luatypo.check_page
% \iffalse
%</sty>
% \fi
-%
-% \clearpage
+% \clearpage
% \section{Configuration file}
%
% \iffalse
@@ -1897,7 +2388,9 @@ return luatypo.check_page
\luatypoBackFuzz=2pt\relax
%% Minimum length of paragraphs’ last lines
-\luatypoLLminWD=2\parindent\relax
+\ifdim\parindent=0pt \luatypoLLminWD=20pt\relax
+\else \luatypoLLminWD=2\parindent\relax
+\fi
%% Maximum number of consecutive hyphenated lines
\luatypoHyphMax=2\relax
@@ -1917,7 +2410,7 @@ return luatypo.check_page
\definecolor{myred}{rgb}{1,0.55,0}
\luatypoSetColor0{red} % Paragraph last full line hyphenated
\luatypoSetColor1{red} % Page last word hyphenated
-\luatypoSetColor2{red} % Hyphens ending two many consecutive lines
+\luatypoSetColor2{red} % Hyphens on to many consecutive lines
\luatypoSetColor3{red} % Short word at end of line
\luatypoSetColor4{cyan} % Widow
\luatypoSetColor5{cyan} % Orphan
@@ -1928,10 +2421,10 @@ return luatypo.check_page
\luatypoSetColor{10}{myred} % First word matches
\luatypoSetColor{11}{myred} % Last word matches
\luatypoSetColor{12}{mygrey}% Paragraph ending on a nearly full line
+\luatypoSetColor{13}{cyan} % Footnote split
-%% Language specific settings (example for French only currently):
+%% Language specific settings (example for French):
%% short words (two letters max) to be avoided at end of lines.
-%% French:
%%\luatypoOneChar{french}{'À à Ô'}
%%\luatypoTwoChars{french}{'Je Tu Il On'}
% \end{macrocode}